pt.c (convert_template_argument): New function, split out from...

* pt.c (convert_template_argument): New function, split out
	from...
	(coerce_template_parms): Here.
	(tsubst): Attempt better error-recovery.

From-SVN: r22099
This commit is contained in:
Mark Mitchell 1998-08-30 10:59:18 +00:00 committed by Mark Mitchell
parent 38f477fe90
commit 8b5b8b7cc1
3 changed files with 268 additions and 266 deletions

View File

@ -1,3 +1,10 @@
1998-08-30 Mark Mitchell <mark@markmitchell.com>
* pt.c (convert_template_argument): New function, split out
from...
(coerce_template_parms): Here.
(tsubst): Attempt better error-recovery.
1998-08-28 Benjamin Kosnik <bkoz@loony.cygnus.com>
* pt.c (decl_template_parm_p): Add checks for

View File

@ -95,6 +95,8 @@ static int type_unification_real PROTO((tree, tree, tree, tree,
static void note_template_header PROTO((int));
static tree maybe_fold_nontype_arg PROTO((tree));
static tree convert_nontype_argument PROTO((tree, tree));
static tree convert_template_argument PROTO ((tree, tree, tree, int,
int , tree));
static tree get_bindings_overload PROTO((tree, tree, tree));
static int for_each_template_parm PROTO((tree, tree_fn_t, void*));
static tree build_template_parm_index PROTO((int, int, int, tree, tree));
@ -2596,11 +2598,207 @@ coerce_template_template_parms (parm_parms, arg_parms, in_decl, outer_args)
return 1;
}
/* Convert all template arguments to their appropriate types, and return
a vector containing the resulting values. If any error occurs, return
error_mark_node, and, if COMPLAIN is non-zero, issue an error message.
Some error messages are issued even if COMPLAIN is zero; for
instance, if a template argument is composed from a local class.
/* Convert the indicated template ARG as necessary to match the
indicated template PARM. Returns the converted ARG, or
error_mark_node if the conversion was unsuccessful. Error messages
are issued if COMPLAIN is non-zero. This conversion is for the Ith
parameter in the parameter list. ARGS is the full set of template
arguments deduced so far. */
static tree
convert_template_argument (parm, arg, args, complain, i, in_decl)
tree parm;
tree arg;
tree args;
int complain;
int i;
tree in_decl;
{
tree val;
tree inner_args;
int is_type, requires_type, is_tmpl_type, requires_tmpl_type;
inner_args = innermost_args (args);
if (TREE_CODE (arg) == TREE_LIST
&& TREE_TYPE (arg) != NULL_TREE
&& TREE_CODE (TREE_TYPE (arg)) == OFFSET_TYPE)
{
/* The template argument was the name of some
member function. That's usually
illegal, but static members are OK. In any
case, grab the underlying fields/functions
and issue an error later if required. */
arg = TREE_VALUE (arg);
TREE_TYPE (arg) = unknown_type_node;
}
requires_tmpl_type = TREE_CODE (parm) == TEMPLATE_DECL;
requires_type = (TREE_CODE (parm) == TYPE_DECL
|| requires_tmpl_type);
/* Check if it is a class template. If REQUIRES_TMPL_TYPE is true,
we also accept implicitly created TYPE_DECL as a valid argument.
This is necessary to handle the case where we pass a template name
to a template template parameter in a scope where we've derived from
in instantiation of that template, so the template name refers to that
instantiation. We really ought to handle this better. */
is_tmpl_type
= ((TREE_CODE (arg) == TEMPLATE_DECL
&& TREE_CODE (DECL_TEMPLATE_RESULT (arg)) == TYPE_DECL)
|| (TREE_CODE (arg) == TEMPLATE_TEMPLATE_PARM
&& !CLASSTYPE_TEMPLATE_INFO (arg))
|| (TREE_CODE (arg) == RECORD_TYPE
&& CLASSTYPE_TEMPLATE_INFO (arg)
&& TREE_CODE (TYPE_NAME (arg)) == TYPE_DECL
&& DECL_ARTIFICIAL (TYPE_NAME (arg))
&& requires_tmpl_type
&& current_class_type
/* FIXME what about nested types? */
&& get_binfo (arg, current_class_type, 0)));
if (is_tmpl_type && TREE_CODE (arg) == TEMPLATE_TEMPLATE_PARM)
arg = TYPE_STUB_DECL (arg);
else if (is_tmpl_type && TREE_CODE (arg) == RECORD_TYPE)
arg = CLASSTYPE_TI_TEMPLATE (arg);
is_type = TREE_CODE_CLASS (TREE_CODE (arg)) == 't' || is_tmpl_type;
if (requires_type && ! is_type && TREE_CODE (arg) == SCOPE_REF
&& TREE_CODE (TREE_OPERAND (arg, 0)) == TEMPLATE_TYPE_PARM)
{
cp_pedwarn ("to refer to a type member of a template parameter,");
cp_pedwarn (" use `typename %E'", arg);
arg = make_typename_type (TREE_OPERAND (arg, 0),
TREE_OPERAND (arg, 1));
is_type = 1;
}
if (is_type != requires_type)
{
if (in_decl)
{
if (complain)
{
cp_error ("type/value mismatch at argument %d in template parameter list for `%D'",
i + 1, in_decl);
if (is_type)
cp_error (" expected a constant of type `%T', got `%T'",
TREE_TYPE (parm),
(is_tmpl_type ? DECL_NAME (arg) : arg));
else
cp_error (" expected a type, got `%E'", arg);
}
}
return error_mark_node;
}
if (is_tmpl_type ^ requires_tmpl_type)
{
if (in_decl && complain)
{
cp_error ("type/value mismatch at argument %d in template parameter list for `%D'",
i + 1, in_decl);
if (is_tmpl_type)
cp_error (" expected a type, got `%T'", DECL_NAME (arg));
else
cp_error (" expected a class template, got `%T'", arg);
}
return error_mark_node;
}
if (is_type)
{
if (requires_tmpl_type)
{
tree parmparm = DECL_INNERMOST_TEMPLATE_PARMS (parm);
tree argparm = DECL_INNERMOST_TEMPLATE_PARMS (arg);
if (coerce_template_template_parms (parmparm, argparm,
in_decl, inner_args))
{
val = arg;
/* TEMPLATE_TEMPLATE_PARM node is preferred over
TEMPLATE_DECL. */
if (val != error_mark_node
&& DECL_TEMPLATE_TEMPLATE_PARM_P (val))
val = TREE_TYPE (val);
}
else
{
if (in_decl && complain)
{
cp_error ("type/value mismatch at argument %d in template parameter list for `%D'",
i + 1, in_decl);
cp_error (" expected a template of type `%D', got `%D'", parm, arg);
}
val = error_mark_node;
}
}
else
{
val = groktypename (arg);
if (! processing_template_decl)
{
/* [basic.link]: A name with no linkage (notably, the
name of a class or enumeration declared in a local
scope) shall not be used to declare an entity with
linkage. This implies that names with no linkage
cannot be used as template arguments. */
tree t = no_linkage_check (val);
if (t)
{
if (ANON_AGGRNAME_P (TYPE_IDENTIFIER (t)))
cp_pedwarn
("template-argument `%T' uses anonymous type", val);
else
cp_error
("template-argument `%T' uses local type `%T'",
val, t);
return error_mark_node;
}
}
}
}
else
{
tree t = tsubst (TREE_TYPE (parm), args, in_decl);
if (processing_template_decl)
arg = maybe_fold_nontype_arg (arg);
if (!uses_template_parms (arg) && !uses_template_parms (t))
/* We used to call digest_init here. However, digest_init
will report errors, which we don't want when complain
is zero. More importantly, digest_init will try too
hard to convert things: for example, `0' should not be
converted to pointer type at this point according to
the standard. Accepting this is not merely an
extension, since deciding whether or not these
conversions can occur is part of determining which
function template to call, or whether a given epxlicit
argument specification is legal. */
val = convert_nontype_argument (t, arg);
else
val = arg;
if (val == NULL_TREE)
val = error_mark_node;
else if (val == error_mark_node && complain)
cp_error ("could not convert template argument `%E' to `%T'",
arg, t);
}
return val;
}
/* Convert all template arguments to their appropriate types, and
return a vector containing the innermost resulting template
arguments. If any error occurs, return error_mark_node, and, if
COMPLAIN is non-zero, issue an error message. Some error messages
are issued even if COMPLAIN is zero; for instance, if a template
argument is composed from a local class.
If REQUIRE_ALL_ARGUMENTS is non-zero, all arguments must be
provided in ARGLIST, or else trailing parameters must have default
@ -2608,19 +2806,20 @@ coerce_template_template_parms (parm_parms, arg_parms, in_decl, outer_args)
deduction for any unspecified trailing arguments. */
static tree
coerce_template_parms (parms, arglist, in_decl,
coerce_template_parms (parms, args, in_decl,
complain,
require_all_arguments)
tree parms, arglist;
tree parms, args;
tree in_decl;
int complain;
int require_all_arguments;
{
int nparms, nargs, i, lost = 0;
tree inner_args;
tree vec;
tree new_args;
tree new_inner_args;
inner_args = innermost_args (arglist);
inner_args = innermost_args (args);
nargs = NUM_TMPL_ARGS (inner_args);
nparms = TREE_VEC_LENGTH (parms);
@ -2641,278 +2840,63 @@ coerce_template_parms (parms, arglist, in_decl,
return error_mark_node;
}
/* Create in VEC the appropriate innermost arguments, and reset
ARGLIST to contain the complete set of arguments. */
if (inner_args && TREE_CODE (inner_args) == TREE_VEC && nargs == nparms)
{
/* If we already have all the arguments, we can just use them.
This is an optimization over the code in the `else' branch
below, and should be functionally identicial. */
vec = copy_node (inner_args);
arglist = add_outermost_template_args (arglist, vec);
}
else
{
/* If we don't already have all the arguments we must get what
we can from default template arguments. The tricky bit is
that previous arguments can influence the default values,
e.g.:
template <class T, class U = T> void foo();
If we see `foo<int>' we have to come up with an {int, int}
vector. */
tree new_arglist;
vec = make_tree_vec (nparms);
new_arglist = add_outermost_template_args (arglist, vec);
for (i = 0; i < nparms; i++)
{
tree arg;
tree parm = TREE_VEC_ELT (parms, i);
if (arglist && TREE_CODE (arglist) == TREE_LIST)
{
arg = arglist;
arglist = TREE_CHAIN (arglist);
if (arg == error_mark_node)
lost++;
else
arg = TREE_VALUE (arg);
}
else if (i < nargs)
{
arg = TREE_VEC_ELT (inner_args, i);
if (arg == error_mark_node)
lost++;
}
/* If no template argument was supplied, look for a default
value. */
else if (TREE_PURPOSE (parm) == NULL_TREE)
{
/* There was no default value. */
my_friendly_assert (!require_all_arguments, 0);
break;
}
else if (TREE_CODE (TREE_VALUE (parm)) == TYPE_DECL)
arg = tsubst (TREE_PURPOSE (parm), new_arglist, in_decl);
else
arg = tsubst_expr (TREE_PURPOSE (parm), new_arglist, in_decl);
TREE_VEC_ELT (vec, i) = arg;
}
/* We've left ARGLIST intact up to this point, in order to allow
iteration through it in the case that it was a TREE_LIST, but
from here on it should contain the full set of template
arguments. */
arglist = new_arglist;
}
new_inner_args = make_tree_vec (nparms);
new_args = add_outermost_template_args (args, new_inner_args);
for (i = 0; i < nparms; i++)
{
tree arg = TREE_VEC_ELT (vec, i);
tree parm = TREE_VALUE (TREE_VEC_ELT (parms, i));
tree val = 0;
int is_type, requires_type, is_tmpl_type, requires_tmpl_type;
tree arg;
tree parm;
/* Get the Ith template parameter. */
parm = TREE_VEC_ELT (parms, i);
/* Calculate the Ith argument. */
if (inner_args && TREE_CODE (inner_args) == TREE_LIST)
{
arg = TREE_VALUE (inner_args);
inner_args = TREE_CHAIN (inner_args);
}
else if (i < nargs)
arg = TREE_VEC_ELT (inner_args, i);
/* If no template argument was supplied, look for a default
value. */
else if (TREE_PURPOSE (parm) == NULL_TREE)
{
/* There was no default value. */
my_friendly_assert (!require_all_arguments, 0);
break;
}
else if (TREE_CODE (TREE_VALUE (parm)) == TYPE_DECL)
arg = tsubst (TREE_PURPOSE (parm), new_args, in_decl);
else
arg = tsubst_expr (TREE_PURPOSE (parm), new_args, in_decl);
/* Now, convert the Ith argument, as necessary. */
if (arg == NULL_TREE)
/* We're out of arguments. */
{
my_friendly_assert (!require_all_arguments, 0);
break;
}
if (arg == error_mark_node)
else if (arg == error_mark_node)
{
cp_error ("template argument %d is invalid", i + 1);
lost++;
continue;
arg = error_mark_node;
}
if (TREE_CODE (arg) == TREE_LIST
&& TREE_TYPE (arg) != NULL_TREE
&& TREE_CODE (TREE_TYPE (arg)) == OFFSET_TYPE)
{
/* The template argument was the name of some
member function. That's usually
illegal, but static members are OK. In any
case, grab the underlying fields/functions
and issue an error later if required. */
arg = TREE_VALUE (arg);
TREE_TYPE (arg) = unknown_type_node;
}
requires_tmpl_type = TREE_CODE (parm) == TEMPLATE_DECL;
requires_type = TREE_CODE (parm) == TYPE_DECL
|| requires_tmpl_type;
/* Check if it is a class template. If REQUIRES_TMPL_TYPE is true,
we also accept implicitly created TYPE_DECL as a valid argument.
This is necessary to handle the case where we pass a template name
to a template template parameter in a scope where we've derived from
in instantiation of that template, so the template name refers to that
instantiation. We really ought to handle this better. */
is_tmpl_type = (TREE_CODE (arg) == TEMPLATE_DECL
&& TREE_CODE (DECL_TEMPLATE_RESULT (arg)) == TYPE_DECL)
|| (TREE_CODE (arg) == TEMPLATE_TEMPLATE_PARM
&& !CLASSTYPE_TEMPLATE_INFO (arg))
|| (TREE_CODE (arg) == RECORD_TYPE
&& CLASSTYPE_TEMPLATE_INFO (arg)
&& TREE_CODE (TYPE_NAME (arg)) == TYPE_DECL
&& DECL_ARTIFICIAL (TYPE_NAME (arg))
&& requires_tmpl_type
&& current_class_type
/* FIXME what about nested types? */
&& get_binfo (arg, current_class_type, 0));
if (is_tmpl_type && TREE_CODE (arg) == TEMPLATE_TEMPLATE_PARM)
arg = TYPE_STUB_DECL (arg);
else if (is_tmpl_type && TREE_CODE (arg) == RECORD_TYPE)
arg = CLASSTYPE_TI_TEMPLATE (arg);
is_type = TREE_CODE_CLASS (TREE_CODE (arg)) == 't' || is_tmpl_type;
if (requires_type && ! is_type && TREE_CODE (arg) == SCOPE_REF
&& TREE_CODE (TREE_OPERAND (arg, 0)) == TEMPLATE_TYPE_PARM)
{
cp_pedwarn ("to refer to a type member of a template parameter,");
cp_pedwarn (" use `typename %E'", arg);
arg = make_typename_type (TREE_OPERAND (arg, 0),
TREE_OPERAND (arg, 1));
is_type = 1;
}
if (is_type != requires_type)
{
if (in_decl)
{
if (complain)
{
cp_error ("type/value mismatch at argument %d in template parameter list for `%D'",
i + 1, in_decl);
if (is_type)
cp_error (" expected a constant of type `%T', got `%T'",
TREE_TYPE (parm),
(is_tmpl_type ? DECL_NAME (arg) : arg));
else
cp_error (" expected a type, got `%E'", arg);
}
}
lost++;
TREE_VEC_ELT (vec, i) = error_mark_node;
continue;
}
if (is_tmpl_type ^ requires_tmpl_type)
{
if (in_decl && complain)
{
cp_error ("type/value mismatch at argument %d in template parameter list for `%D'",
i + 1, in_decl);
if (is_tmpl_type)
cp_error (" expected a type, got `%T'", DECL_NAME (arg));
else
cp_error (" expected a class template, got `%T'", arg);
}
lost++;
TREE_VEC_ELT (vec, i) = error_mark_node;
continue;
}
if (is_type)
{
if (requires_tmpl_type)
{
tree parmparm = DECL_INNERMOST_TEMPLATE_PARMS (parm);
tree argparm = DECL_INNERMOST_TEMPLATE_PARMS (arg);
if (coerce_template_template_parms (parmparm, argparm,
in_decl, vec))
{
val = arg;
/* TEMPLATE_TEMPLATE_PARM node is preferred over
TEMPLATE_DECL. */
if (val != error_mark_node
&& DECL_TEMPLATE_TEMPLATE_PARM_P (val))
val = TREE_TYPE (val);
}
else
{
if (in_decl && complain)
{
cp_error ("type/value mismatch at argument %d in template parameter list for `%D'",
i + 1, in_decl);
cp_error (" expected a template of type `%D', got `%D'", parm, arg);
}
val = error_mark_node;
}
}
else
{
val = groktypename (arg);
if (! processing_template_decl)
{
/* [basic.link]: A name with no linkage (notably, the
name of a class or enumeration declared in a local
scope) shall not be used to declare an entity with
linkage. This implies that names with no linkage
cannot be used as template arguments. */
tree t = no_linkage_check (val);
if (t)
{
if (ANON_AGGRNAME_P (TYPE_IDENTIFIER (t)))
cp_pedwarn
("template-argument `%T' uses anonymous type", val);
else
cp_error
("template-argument `%T' uses local type `%T'",
val, t);
return error_mark_node;
}
}
}
}
else
{
tree t = tsubst (TREE_TYPE (parm), arglist, in_decl);
if (processing_template_decl)
arg = maybe_fold_nontype_arg (arg);
if (!uses_template_parms (arg) && !uses_template_parms (t))
/* We used to call digest_init here. However, digest_init
will report errors, which we don't want when complain
is zero. More importantly, digest_init will try too
hard to convert things: for example, `0' should not be
converted to pointer type at this point according to
the standard. Accepting this is not merely an
extension, since deciding whether or not these
conversions can occur is part of determining which
function template to call, or whether a given epxlicit
argument specification is legal. */
val = convert_nontype_argument (t, arg);
else
val = arg;
if (val == NULL_TREE)
val = error_mark_node;
else if (val == error_mark_node && complain)
cp_error ("could not convert template argument `%E' to `%T'",
arg, t);
}
if (val == error_mark_node)
else
arg = convert_template_argument (TREE_VALUE (parm),
arg, new_args, complain, i,
in_decl);
if (arg == error_mark_node)
lost++;
TREE_VEC_ELT (vec, i) = val;
TREE_VEC_ELT (new_inner_args, i) = arg;
}
if (lost)
return error_mark_node;
return vec;
return new_inner_args;
}
/* Renturns 1 iff the OLDARGS and NEWARGS are in fact identical sets
@ -5317,7 +5301,9 @@ tsubst (t, args, in_decl)
if (level <= levels)
arg = TMPL_ARG (args, level, idx);
if (arg != NULL_TREE)
if (arg == error_mark_node)
return error_mark_node;
else if (arg != NULL_TREE)
{
if (TREE_CODE (t) == TEMPLATE_TYPE_PARM)
{

View File

@ -0,0 +1,9 @@
// Build don't link:
template <class T>
struct S1 {};
template <class T, class U = S1<T> >
struct S2 {};
template struct S2<100>; // ERROR - type/value mismatch