* class.c (grow_method): Remove check for redeclaration.
Fri Sep 5 01:37:17 1997 Mark Mitchell <mmitchell@usa.net>
* cp-tree.h (INNERMOST_TEMPLATE_PARMS): New macro.
(DECL_INNERMOST_TEMPLATE_PARMS): Likewise.
(PRIMARY_TEMPLATE_P): Use it.
* call.c (build_overload_call_real): Use it.
* class.c (instantiate_type): Likewise.
* decl.c (decls_match): Likewise.
* method.c (build_overload_identifier): Likewise.
* pt.c (push_template_decl): Likewise.
(classtype_mangled_name): Likewise.
(lookup_template_class): Likewise.
* cp-tree.h (DECL_NTPARMS): Change name from DECL_NT_PARMS to
DECL_NTPARMS to conform to usage elsewhere.
* call.c (add_template_candidate): Likewise.
* class.c (instantiate_type): Likewise.
* pt.c (instantiate_template): Likewise.
(get_bindings): Likewise.
* class.c (grow_method): Use DECL_FUNCTION_TEMPLATE_P instead of
is_member_template.
* pt.c (unify): Undo changes to allow multiple levels of template
parameters.
(type_unification): Likewise.
(fn_type_unification): Likewise.
(get_class_bindings): Likewise.
* cp-tree.h (Likewise).
* decl.c (replace_defarg): Check that the type of the default
parameter does not invlove a template type before complaining
about the initialization.
* error.c (dump_expr): Deal with template constant parameters in
member templates correctly.
* pt.c (is_member_template): Deal with class specializations
correctly.
(tsubst): Handle "partial instantiation" of member templates
correctly.
Wed Sep 3 12:30:24 1997 Mark Mitchell <mmitchell@usa.net>
* pt.c (type_unification): Change calling squence to allow for
multiple levels of template parameters.
(tsubst_expr): Likewise.
(tsubst): Likewise.
(tsubst_copy): Likewise.
(instantiate_template): Likewise.
(unify): Likewise.
* call.c (build_overload_call_real): Use it.
(add_builtin_candidate): Use it.
(build_new_method_call): Use it.
* class.c (instantiate_type): Use it.
* decl.c (grokdeclarator): Use it.
* decl2.c (finish_file): Use it.
* method.c (build_overload_identifier): Use it.
* call.c (add_template_candidate): Add additional parameter for
the function return type. Call fn_type_unification istead of
type_unification.
(build_user_type_conversion_1): Handle member templates.
(build_new_function_call): Likewise.
(build_new_op): Likewise.
(build_new_method_call): Likewise.
* class.c (grow_method): Don't give an error message indicating
that two member templates with the same name are ambiguous.
(finish_struct): Treat member template functions just like member
functions.
* cp-tree.h (check_member_template): Add declaration.
(begin_member_template_processing): Likewise.
(end_member_template_processing): Likewise.
(fn_type_unification): Likewise.
(is_member_template): Likewise.
(tsubst): Change prototype.
(tsubst_expr): Likewise.
(tsubst_copy): Likewise.
(instantiate_template): Likewise.
(get_bindings): Likewise.
* decl.c (decls_match): Handle multiple levels of template
parameters.
(pushdecl): Handle template type params just like other type
declarations.
(push_class_level_binding): Return immediately if the
class_binding_level is NULL.
(grokfndecl): If check_classfn() returns a member_template, use
the result of the template, not the template itself.
* decl2.c (check_member_template): New function. Check to see
that the entity declared to be a member template can be one.
(check_classfn): Allow redeclaration of member template functions
with different types; the new functions can be specializations or
explicit instantiations.
* error.c (dump_decl): Handle multiple levels of template
parameters.
(dump_function_decl): Update to handle function templates.
* lex.c (do_pending_inlines): Set up template parameter context
for member templates.
(process_next_inline): Likewise.
* method. (build_overload_identifier): Adjust for multiple levels
of template parameters.
* parse.y (fn.def2): Add member templates.
(component_decl_1): Likewise.
* pt.c (begin_member_template_processing): New function.
(end_member_template_processing): Likewise.
(is_member_template): Likewise.
(fn_type_unification): Likewise.
(current_template_parms): Return a vector of all the template
parms, not just the innermost level of parms.
(push_template_decl): Deal with the possibility of member
templates.
(lookup_template_class): Likewise.
(uses_template_parms): Likewise.
(tsubst): Modify processing to TEMPLATE_TYPE_PARM and
TEMPLATE_CONST_PARM to deal with multiple levels of template
arguments. Add processing of TEMPLATE_DECL to produce new
TEMPLATE_DECLs from old ones.
(do_decl_instantiation): Handle member templates.
* search.c (lookup_fnfields_1): Handle member template conversion
operators.
* tree.c (cp_tree_equal): Check the levels, as well as the
indices, of TEMPLATE_CONST_PARMs.
* typeck.c (comptypes): Check the levels, as well as the indices,
fo TEMPLATE_TYPE_PARMs.
(build_x_function_call): Treat member templates like member
functions.
Member templates.
From-SVN: r15130
+Fri Sep 5 17:27:38 1997 Jason Merrill <jason@yorick.cygnus.com>
+
+ * class.c (grow_method): Remove check for redeclaration.
+
+Wed Sep 3 12:30:24 1997 Mark Mitchell <mmitchell@usa.net>
+
+ * pt.c (type_unification): Change calling squence to allow for
+ multiple levels of template parameters.
+ (tsubst_expr): Likewise.
+ (tsubst): Likewise.
+ (tsubst_copy): Likewise.
+ (instantiate_template): Likewise.
+ (unify): Likewise.
+ * call.c (build_overload_call_real): Use it.
+ (add_builtin_candidate): Use it.
+ (build_new_method_call): Use it.
+ * class.c (instantiate_type): Use it.
+ * decl.c (grokdeclarator): Use it.
+ * decl2.c (finish_file): Use it.
+ * method.c (build_overload_identifier): Use it.
+
+ * call.c (add_template_candidate): Add additional parameter for
+ the function return type. Call fn_type_unification istead of
+ type_unification.
+ (build_user_type_conversion_1): Handle member templates.
+ (build_new_function_call): Likewise.
+ (build_new_op): Likewise.
+ (build_new_method_call): Likewise.
+
+ * class.c (grow_method): Don't give an error message indicating
+ that two member templates with the same name are ambiguous.
+ (finish_struct): Treat member template functions just like member
+ functions.
+
+ * cp-tree.h (check_member_template): Add declaration.
+ (begin_member_template_processing): Likewise.
+ (end_member_template_processing): Likewise.
+ (fn_type_unification): Likewise.
+ (is_member_template): Likewise.
+ (tsubst): Change prototype.
+ (tsubst_expr): Likewise.
+ (tsubst_copy): Likewise.
+ (instantiate_template): Likewise.
+ (get_bindings): Likewise.
+
+ * decl.c (decls_match): Handle multiple levels of template
+ parameters.
+ (pushdecl): Handle template type params just like other type
+ declarations.
+ (push_class_level_binding): Return immediately if the
+ class_binding_level is NULL.
+ (grokfndecl): If check_classfn() returns a member_template, use
+ the result of the template, not the template itself.
+
+ * decl2.c (check_member_template): New function. Check to see
+ that the entity declared to be a member template can be one.
+ (check_classfn): Allow redeclaration of member template functions
+ with different types; the new functions can be specializations or
+ explicit instantiations.
+
+ * error.c (dump_decl): Handle multiple levels of template
+ parameters.
+ (dump_function_decl): Update to handle function templates.
+
+ * lex.c (do_pending_inlines): Set up template parameter context
+ for member templates.
+ (process_next_inline): Likewise.
+
+ * method. (build_overload_identifier): Adjust for multiple levels
+ of template parameters.
+
+ * parse.y (fn.def2): Add member templates.
+ (component_decl_1): Likewise.
+
+ * pt.c (begin_member_template_processing): New function.
+ (end_member_template_processing): Likewise.
+ (is_member_template): Likewise.
+ (fn_type_unification): Likewise.
+ (current_template_parms): Return a vector of all the template
+ parms, not just the innermost level of parms.
+ (push_template_decl): Deal with the possibility of member
+ templates.
+ (lookup_template_class): Likewise.
+ (uses_template_parms): Likewise.
+ (tsubst): Modify processing to TEMPLATE_TYPE_PARM and
+ TEMPLATE_CONST_PARM to deal with multiple levels of template
+ arguments. Add processing of TEMPLATE_DECL to produce new
+ TEMPLATE_DECLs from old ones.
+ (do_decl_instantiation): Handle member templates.
+
+ * search.c (lookup_fnfields_1): Handle member template conversion
+ operators.
+
+ * tree.c (cp_tree_equal): Check the levels, as well as the
+ indices, of TEMPLATE_CONST_PARMs.
+
+ * typeck.c (comptypes): Check the levels, as well as the indices,
+ fo TEMPLATE_TYPE_PARMs.
+ (build_x_function_call): Treat member templates like member
+ functions.
+
Wed Sep 3 11:09:25 1997 Jason Merrill <jason@yorick.cygnus.com>
* typeck.c (c_expand_return): Always convert_for_initialization
static struct z_candidate * splice_viable PROTO((struct z_candidate *));
static int any_viable PROTO((struct z_candidate *));
static struct z_candidate * add_template_candidate
- PROTO((struct z_candidate *, tree, tree, int));
+ PROTO((struct z_candidate *, tree, tree, tree, int));
static struct z_candidate * add_builtin_candidates
PROTO((struct z_candidate *, enum tree_code, enum tree_code,
tree, tree *, int));
}
if (TREE_CODE (function) == TEMPLATE_DECL)
{
- int ntparms = TREE_VEC_LENGTH (DECL_TEMPLATE_PARMS (function));
- tree *targs = (tree *) alloca (sizeof (tree) * ntparms);
+ int ntparms = DECL_NTPARMS (function);
+ tree targs = make_tree_vec (ntparms);
int i;
- i = type_unification (DECL_TEMPLATE_PARMS (function), targs,
+ i = type_unification (DECL_INNERMOST_TEMPLATE_PARMS (function),
+ &TREE_VEC_ELT (targs, 0),
TYPE_ARG_TYPES (TREE_TYPE (function)),
parms, &template_cost, 0, 0);
if (i == 0)
}
static struct z_candidate *
-add_template_candidate (candidates, tmpl, arglist, flags)
+add_template_candidate (candidates, tmpl, arglist, return_type, flags)
struct z_candidate *candidates;
- tree tmpl, arglist;
+ tree tmpl, arglist, return_type;
int flags;
{
- int ntparms = TREE_VEC_LENGTH (DECL_TEMPLATE_PARMS (tmpl));
- tree *targs = (tree *) alloca (sizeof (tree) * ntparms);
+ int ntparms = DECL_NTPARMS (tmpl);
+ tree targs = make_tree_vec (ntparms);
struct z_candidate *cand;
- int i, dummy = 0;
+ int i;
tree fn;
- i = type_unification (DECL_TEMPLATE_PARMS (tmpl), targs,
- TYPE_ARG_TYPES (TREE_TYPE (tmpl)),
- arglist, &dummy, 0, 0);
+ i = fn_type_unification (tmpl, targs, arglist, return_type, 0);
+
if (i != 0)
return candidates;
tree fromtype = TREE_TYPE (expr);
tree ctors = NULL_TREE, convs = NULL_TREE, *p;
tree args;
+ tree templates = NULL_TREE;
if (IS_AGGR_TYPE (totype))
ctors = lookup_fnfields (TYPE_BINFO (totype), ctor_identifier, 0);
if (DECL_NONCONVERTING_P (ctors))
continue;
- candidates = add_function_candidate (candidates, ctors, args, flags);
- candidates->second_conv = build1 (IDENTITY_CONV, totype, NULL_TREE);
- candidates->basetype_path = TYPE_BINFO (totype);
+ if (TREE_CODE (ctors) == TEMPLATE_DECL)
+ {
+ templates = decl_tree_cons (NULL_TREE, ctors, templates);
+ candidates =
+ add_template_candidate (candidates, ctors,
+ args, NULL_TREE, flags);
+ }
+ else
+ candidates = add_function_candidate (candidates, ctors,
+ args, flags);
+
+ if (candidates)
+ {
+ candidates->second_conv = build1 (IDENTITY_CONV, totype, NULL_TREE);
+ candidates->basetype_path = TYPE_BINFO (totype);
+ }
}
if (convs)
else if (ics)
for (; fn; fn = DECL_CHAIN (fn))
{
- candidates = add_function_candidate (candidates, fn, args, flags);
- candidates->second_conv = ics;
- candidates->basetype_path = TREE_PURPOSE (convs);
- if (candidates->viable == 1 && ICS_BAD_FLAG (ics))
- candidates->viable = -1;
+ if (TREE_CODE (fn) == TEMPLATE_DECL)
+ {
+ templates = decl_tree_cons (NULL_TREE, fn, templates);
+ candidates =
+ add_template_candidate (candidates, fn, args,
+ totype, flags);
+ }
+ else
+ candidates = add_function_candidate (candidates, fn,
+ args, flags);
+
+ if (candidates)
+ {
+ candidates->second_conv = ics;
+ candidates->basetype_path = TREE_PURPOSE (convs);
+ if (candidates->viable == 1 && ICS_BAD_FLAG (ics))
+ candidates->viable = -1;
+ }
}
}
for (p = &(cand->second_conv); TREE_CODE (*p) != IDENTITY_CONV; )
p = &(TREE_OPERAND (*p, 0));
+ /* Pedantically, normal function declarations are never considered
+ to refer to template instantiations, but we won't implement that
+ until we implement full template instantiation syntax. */
+ if (templates && ! cand->template && ! DECL_INITIAL (cand->fn)
+ && TREE_CODE (TREE_TYPE (cand->fn)) != METHOD_TYPE)
+ add_maybe_template (cand->fn, templates);
+
*p = build
(USER_CONV,
(DECL_CONSTRUCTOR_P (cand->fn)
{
templates = decl_tree_cons (NULL_TREE, t, templates);
candidates = add_template_candidate
- (candidates, t, args, LOOKUP_NORMAL);
+ (candidates, t, args, NULL_TREE, LOOKUP_NORMAL);
}
else
candidates = add_function_candidate
{
templates = decl_tree_cons (NULL_TREE, fns, templates);
candidates = add_template_candidate
- (candidates, fns, arglist, flags);
+ (candidates, fns, arglist, TREE_TYPE (fnname), flags);
}
else
candidates = add_function_candidate (candidates, fns, arglist, flags);
mem_arglist = tree_cons (NULL_TREE, build_this (arg1), TREE_CHAIN (arglist));
for (; fn; fn = DECL_CHAIN (fn))
{
+ tree this_arglist;
+
if (TREE_CODE (TREE_TYPE (fn)) == METHOD_TYPE)
- candidates = add_function_candidate
- (candidates, fn, mem_arglist, flags);
+ this_arglist = mem_arglist;
else
- candidates = add_function_candidate (candidates, fn, arglist, flags);
-
- candidates->basetype_path = TREE_PURPOSE (fns);
+ this_arglist = arglist;
+
+ if (TREE_CODE (fn) == TEMPLATE_DECL)
+ {
+ /* A member template. */
+ templates = decl_tree_cons (NULL_TREE, fn, templates);
+ candidates = add_template_candidate
+ (candidates, fn, this_arglist,
+ TREE_TYPE (fnname), LOOKUP_NORMAL);
+ }
+ else
+ candidates = add_function_candidate
+ (candidates, fn, this_arglist, flags);
+
+ if (candidates)
+ candidates->basetype_path = TREE_PURPOSE (fns);
}
}
if (DECL_FUNCTION_MEMBER_P (cand->fn))
enforce_access (cand->basetype_path, cand->fn);
- /* Pedantically, it is ill-formed to define a function that could
- also be a template instantiation, but we won't implement that
- until things settle down. */
+ /* Pedantically, normal function declarations are never considered
+ to refer to template instantiations, but we won't implement that
+ until we implement full template instantiation syntax. */
if (templates && ! cand->template && ! DECL_INITIAL (cand->fn)
&& TREE_CODE (TREE_TYPE (cand->fn)) != METHOD_TYPE)
add_maybe_template (cand->fn, templates);
if (DECL_TEMPLATE_INFO (fn))
/* This came from a template. Instantiate the default arg here,
not in tsubst. */
- arg = tsubst_expr (arg,
- &TREE_VEC_ELT (DECL_TI_ARGS (fn), 0),
+ arg = tsubst_expr (arg, DECL_TI_ARGS (fn),
TREE_VEC_LENGTH (DECL_TI_ARGS (fn)), NULL_TREE);
converted_args = tree_cons
(NULL_TREE, convert_default_arg (TREE_VALUE (parm), arg),
tree basetype, mem_args, fns, instance_ptr;
tree pretty_name;
tree user_args = args;
+ tree templates = NULL_TREE;
/* If there is an extra argument for controlling virtual bases,
remove it for error reporting. */
mem_args = tree_cons (NULL_TREE, instance_ptr, args);
for (; t; t = DECL_CHAIN (t))
{
+ tree this_arglist;
+
/* We can end up here for copy-init of same or base class. */
if (name == ctor_identifier
&& (flags & LOOKUP_ONLYCONVERTING)
&& DECL_NONCONVERTING_P (t))
continue;
if (TREE_CODE (TREE_TYPE (t)) == METHOD_TYPE)
- candidates = add_function_candidate
- (candidates, t, mem_args, flags);
+ this_arglist = mem_args;
else
- candidates = add_function_candidate (candidates, t, args, flags);
- candidates->basetype_path = TREE_PURPOSE (fns);
+ this_arglist = args;
+
+ if (TREE_CODE (t) == TEMPLATE_DECL)
+ {
+ /* A member template. */
+ templates = decl_tree_cons (NULL_TREE, t, templates);
+ candidates =
+ add_template_candidate (candidates, t,
+ this_arglist,
+ TREE_TYPE (name),
+ LOOKUP_NORMAL);
+ }
+ else
+ candidates = add_function_candidate (candidates, t,
+ this_arglist, flags);
+
+ if (candidates)
+ candidates->basetype_path = TREE_PURPOSE (fns);
}
}
|| resolves_to_fixed_type_p (instance, 0)))
flags |= LOOKUP_NONVIRTUAL;
+ /* Pedantically, normal function declarations are never considered
+ to refer to template instantiations, but we won't implement that
+ until we implement full template instantiation syntax. */
+ if (templates && ! cand->template && ! DECL_INITIAL (cand->fn))
+ add_maybe_template (cand->fn, templates);
+
return build_over_call
(cand->fn, cand->convs,
TREE_CODE (TREE_TYPE (cand->fn)) == METHOD_TYPE ? mem_args : args,
if (testp < (tree *) obstack_next_free (&class_obstack))
{
- tree x, prev_x;
-
- for (x = *testp; x; x = DECL_CHAIN (x))
- {
- if (DECL_NAME (fndecl) == ansi_opname[(int) DELETE_EXPR]
- || DECL_NAME (fndecl) == ansi_opname[(int) VEC_DELETE_EXPR])
- {
- /* ANSI C++ June 5 1992 WP 12.5.5.1 */
- cp_error_at ("`%D' overloaded", fndecl);
- cp_error_at ("previous declaration as `%D' here", x);
- }
- if (DECL_ASSEMBLER_NAME (fndecl) == DECL_ASSEMBLER_NAME (x))
- {
- /* Friend-friend ambiguities are warned about outside
- this loop. */
- cp_error_at ("ambiguous method `%#D' in structure", fndecl);
- break;
- }
- prev_x = x;
- }
- if (x == 0)
- {
- if (*testp)
- DECL_CHAIN (prev_x) = fndecl;
- else
- *testp = fndecl;
- }
+ tree *p;
+ for (p = testp; *p; )
+ p = &DECL_CHAIN (*p);
+ *p = fndecl;
}
else
{
}
}
- if (TREE_CODE (x) == FUNCTION_DECL)
+ if (TREE_CODE (x) == FUNCTION_DECL
+ || DECL_FUNCTION_TEMPLATE_P (x))
{
DECL_CLASS_CONTEXT (x) = t;
if (last_x)
for (elem = get_first_fn (rhs); elem; elem = DECL_CHAIN (elem))
if (TREE_CODE (elem) == TEMPLATE_DECL)
{
- int n = TREE_VEC_LENGTH (DECL_TEMPLATE_PARMS (elem));
- tree *t = (tree *) alloca (sizeof (tree) * n);
+ int n = DECL_NTPARMS (elem);
+ tree t = make_tree_vec (n);
int i, d = 0;
- i = type_unification (DECL_TEMPLATE_PARMS (elem), t,
- TYPE_ARG_TYPES (TREE_TYPE (elem)),
- TYPE_ARG_TYPES (lhstype), &d, 0, 1);
+ i = type_unification
+ (DECL_INNERMOST_TEMPLATE_PARMS (elem),
+ &TREE_VEC_ELT (t, 0), TYPE_ARG_TYPES (TREE_TYPE (elem)),
+ TYPE_ARG_TYPES (lhstype), &d, 0, 1);
if (i == 0)
{
if (save_elem)
/* Index into a template parameter list. This parameter must not be a
type. */
-DEFTREECODE (TEMPLATE_CONST_PARM, "template_const_parm", "c", 2)
+DEFTREECODE (TEMPLATE_CONST_PARM, "template_const_parm", "c", 3)
/* A thunk is a stub function.
#define CLASSTYPE_TI_TEMPLATE(NODE) TI_TEMPLATE (CLASSTYPE_TEMPLATE_INFO (NODE))
#define CLASSTYPE_TI_ARGS(NODE) TI_ARGS (CLASSTYPE_TEMPLATE_INFO (NODE))
#define CLASSTYPE_TI_SPEC_INFO(NODE) TI_SPEC_INFO (CLASSTYPE_TEMPLATE_INFO (NODE))
+#define INNERMOST_TEMPLATE_PARMS(NODE) TREE_VALUE(NODE)
#define DECL_SAVED_TREE(NODE) DECL_MEMFUNC_POINTER_TO (NODE)
#define COMPOUND_STMT_NO_SCOPE(NODE) TREE_LANG_FLAG_0 (NODE)
/* Accessor macros for C++ template decl nodes. */
#define DECL_TEMPLATE_PARMS(NODE) DECL_ARGUMENTS(NODE)
+#define DECL_INNERMOST_TEMPLATE_PARMS(NODE) \
+ INNERMOST_TEMPLATE_PARMS (DECL_TEMPLATE_PARMS (NODE))
+#define DECL_NTPARMS(NODE) \
+ TREE_VEC_LENGTH (DECL_INNERMOST_TEMPLATE_PARMS (NODE))
/* For class templates. */
#define DECL_TEMPLATE_SPECIALIZATIONS(NODE) DECL_SIZE(NODE)
/* For function, method, class-data templates. */
&& TREE_CODE (DECL_TEMPLATE_RESULT (NODE)) == FUNCTION_DECL)
#define PRIMARY_TEMPLATE_P(NODE) \
- (TREE_TYPE (DECL_TEMPLATE_PARMS (NODE)) == (NODE))
+ (TREE_TYPE (DECL_INNERMOST_TEMPLATE_PARMS (NODE)) == (NODE))
#define CLASSTYPE_TEMPLATE_LEVEL(NODE) \
(TREE_INT_CST_HIGH (TREE_PURPOSE (CLASSTYPE_TI_TEMPLATE (NODE))))
extern tree grok_array_decl PROTO((tree, tree));
extern tree delete_sanity PROTO((tree, tree, int, int));
extern tree check_classfn PROTO((tree, tree));
+extern void check_member_template PROTO((tree));
extern tree grokfield PROTO((tree, tree, tree, tree, tree));
extern tree grokbitfield PROTO((tree, tree, tree));
extern tree groktypefield PROTO((tree, tree));
extern tree get_id_2 PROTO((char *, tree));
/* in pt.c */
-extern tree tsubst PROTO ((tree, tree*, int, tree));
-extern tree tsubst_expr PROTO ((tree, tree*, int, tree));
-extern tree tsubst_copy PROTO ((tree, tree*, int, tree));
+extern tree tsubst PROTO ((tree, tree, int, tree));
+extern tree tsubst_expr PROTO ((tree, tree, int, tree));
+extern tree tsubst_copy PROTO ((tree, tree, int, tree));
extern tree tsubst_chain PROTO((tree, tree));
+extern void begin_member_template_processing PROTO((tree));
+extern void end_member_template_processing PROTO((void));
extern void begin_template_parm_list PROTO((void));
extern tree process_template_parm PROTO((tree, tree));
extern tree end_template_parm_list PROTO((tree));
extern tree lookup_template_class PROTO((tree, tree, tree));
extern int uses_template_parms PROTO((tree));
extern tree instantiate_class_template PROTO((tree));
-extern tree instantiate_template PROTO((tree, tree *));
+extern tree instantiate_template PROTO((tree, tree));
extern void overload_template_name PROTO((tree));
+extern int fn_type_unification PROTO((tree, tree, tree, tree, int));
extern int type_unification PROTO((tree, tree *, tree, tree, int *, int, int));
struct tinst_level *tinst_for_decl PROTO((void));
extern void mark_decl_instantiated PROTO((tree, int));
extern tree instantiate_decl PROTO((tree));
extern tree lookup_nested_type_by_name PROTO((tree, tree));
extern tree do_poplevel PROTO((void));
-extern tree *get_bindings PROTO((tree, tree));
+extern tree get_bindings PROTO((tree, tree));
/* CONT ... */
extern void add_tree PROTO((tree));
extern void add_maybe_template PROTO((tree, tree));
extern tree most_specialized_class PROTO((tree, tree));
extern int more_specialized_class PROTO((tree, tree));
extern void do_pushlevel PROTO((void));
+extern int is_member_template PROTO((tree));
/* in repo.c */
extern void repo_template_used PROTO((tree));
{
tree newargs = DECL_TEMPLATE_PARMS (newdecl);
tree oldargs = DECL_TEMPLATE_PARMS (olddecl);
- int i, len = TREE_VEC_LENGTH (newargs);
+ int i;
- if (TREE_VEC_LENGTH (oldargs) != len)
- return 0;
-
- for (i = 0; i < len; i++)
+ /* Run through all the levels of template parmaters, checking
+ that they match. */
+ while (newargs && oldargs)
{
- tree newarg = TREE_VALUE (TREE_VEC_ELT (newargs, i));
- tree oldarg = TREE_VALUE (TREE_VEC_ELT (oldargs, i));
- if (TREE_CODE (newarg) != TREE_CODE (oldarg))
- return 0;
- else if (TREE_CODE (newarg) == TYPE_DECL)
- /* continue */;
- else if (! comptypes (TREE_TYPE (newarg), TREE_TYPE (oldarg), 1))
+ int len = TREE_VEC_LENGTH (INNERMOST_TEMPLATE_PARMS (newargs));
+
+ if (TREE_VEC_LENGTH (INNERMOST_TEMPLATE_PARMS (oldargs)) != len)
return 0;
+
+ for (i = 0; i < len; i++)
+ {
+ tree newarg =
+ TREE_VALUE (TREE_VEC_ELT
+ (INNERMOST_TEMPLATE_PARMS (newargs), i));
+ tree oldarg =
+ TREE_VALUE (TREE_VEC_ELT
+ (INNERMOST_TEMPLATE_PARMS (oldargs), i));
+ if (TREE_CODE (newarg) != TREE_CODE (oldarg))
+ return 0;
+ else if (TREE_CODE (newarg) == TYPE_DECL)
+ /* continue */;
+ else if (! comptypes (TREE_TYPE (newarg), TREE_TYPE (oldarg), 1))
+ return 0;
+ }
+ newargs = TREE_CHAIN (newargs);
+ oldargs = TREE_CHAIN (oldargs);
}
+ if ((newargs == NULL_TREE) != (oldargs == NULL_TREE))
+ /* One declaration has more levels that the other. */
+ return 0;
+
if (TREE_CODE (DECL_TEMPLATE_RESULT (newdecl)) == TYPE_DECL)
types_match = 1;
else
/* Type are looked up using the DECL_NAME, as that is what the rest of the
compiler wants to use. */
if (TREE_CODE (x) == TYPE_DECL || TREE_CODE (x) == VAR_DECL
- || TREE_CODE (x) == NAMESPACE_DECL)
+ || TREE_CODE (x) == NAMESPACE_DECL || TREE_CODE (x) == TEMPLATE_TYPE_PARM)
name = DECL_NAME (x);
if (name)
tree name;
tree x;
{
+ /* The class_binding_level will be NULL if x is a template
+ parameter name in a member template. */
+ if (!class_binding_level)
+ return;
+
if (TREE_CODE (x) == TYPE_DECL && DECL_ARTIFICIAL (x)
&& purpose_member (name, class_binding_level->class_shadowed))
return;
if (check)
{
tmp = check_classfn (ctype, decl);
+
+ if (tmp && TREE_CODE (tmp) == TEMPLATE_DECL)
+ tmp = DECL_TEMPLATE_RESULT(tmp);
+
if (tmp && DECL_ARTIFICIAL (tmp))
cp_error ("definition of implicitly-declared `%D'", tmp);
if (tmp && duplicate_decls (decl, tmp))
if (ctype != NULL_TREE && check)
{
tmp = check_classfn (ctype, decl);
+
+ if (tmp && TREE_CODE (tmp) == TEMPLATE_DECL)
+ tmp = DECL_TEMPLATE_RESULT(tmp);
+
if (tmp && DECL_STATIC_FUNCTION_P (tmp)
&& TREE_CODE (TREE_TYPE (decl)) == METHOD_TYPE)
{
&& uses_template_parms (current_class_type))
{
tree args = current_template_args ();
- type = tsubst (type, &TREE_VEC_ELT (args, 0),
- TREE_VEC_LENGTH (args), NULL_TREE);
+ type = tsubst (type, args,
+ TREE_VEC_LENGTH (TREE_VEC_ELT
+ (args, 0)),
+ NULL_TREE);
}
/* This pop_nested_class corresponds to the
replace_defarg (arg, init)
tree arg, init;
{
- if (! processing_template_decl
+ if (! processing_template_decl && ! uses_template_parms (TREE_VALUE (arg))
&& ! can_convert_arg (TREE_VALUE (arg), TREE_TYPE (init), init))
cp_pedwarn ("invalid type `%T' for default argument to `%T'",
TREE_TYPE (init), TREE_VALUE (arg));
}
}
+/* Report an error if the indicated template declaration is not the
+ sort of thing that should be a member template. */
+
+void
+check_member_template (tmpl)
+ tree tmpl;
+{
+ tree decl;
+
+ my_friendly_assert (TREE_CODE (tmpl) == TEMPLATE_DECL, 0);
+ decl = DECL_TEMPLATE_RESULT (tmpl);
+
+ if (TREE_CODE (decl) == FUNCTION_DECL)
+ {
+ if (current_function_decl)
+ /* 14.5.2.2 [temp.mem]
+
+ A local class shall not have member templates. */
+ cp_error ("declaration of of member template `%#D' in local class",
+ decl);
+
+ if (DECL_VIRTUAL_P (decl))
+ {
+ /* 14.5.2.3 [temp.mem]
+
+ A member function template shall not be virtual. */
+ cp_error
+ ("invalid use of `virtual' in template declaration of `%#D'",
+ decl);
+ DECL_VIRTUAL_P (decl) = 0;
+ }
+
+ /* The debug-information generating code doesn't know what to do
+ with member templates. */
+ DECL_IGNORED_P (tmpl) = 1;
+ }
+ else if (TREE_CODE (decl) == TYPE_DECL &&
+ AGGREGATE_TYPE_P (TREE_TYPE (decl)))
+ {
+ if (current_function_decl)
+ /* 14.5.2.2 [temp.mem]
+
+ A local class shall not have member templates. */
+ cp_error ("declaration of of member template `%#D' in local class",
+ decl);
+
+ /* We don't handle member template classes yet. */
+ sorry ("member templates classes");
+ }
+ else
+ cp_error ("template declaration of `%#D'", decl);
+}
+
/* Sanity check: report error if this function FUNCTION is not
really a member of the class (CTYPE) it is supposed to belong to.
CNAME is the same here as it is for grokclassfn above. */
tree method_vec = CLASSTYPE_METHOD_VEC (complete_type (ctype));
tree *methods = 0;
tree *end = 0;
+ tree templates = NULL_TREE;
if (method_vec != 0)
{
while (++methods != end)
{
+ fndecl = *methods;
if (fn_name == DECL_NAME (*methods))
{
got_it:
TREE_TYPE (TREE_TYPE (fndecl)), 1)
&& compparms (p1, p2, 3))
return fndecl;
+
+ if (is_member_template (fndecl))
+ /* This function might be an instantiation
+ or specialization of fndecl. */
+ templates =
+ tree_cons (NULL_TREE, fndecl, templates);
}
#endif
fndecl = DECL_CHAIN (fndecl);
}
break; /* loser */
}
- }
- }
+ else if (TREE_CODE (fndecl) == TEMPLATE_DECL
+ && IDENTIFIER_TYPENAME_P (DECL_NAME (fndecl))
+ && IDENTIFIER_TYPENAME_P (fn_name))
+ /* The method in the class is a member template
+ conversion operator. We are declaring another
+ conversion operator. It is possible that even though
+ the names don't match, there is some specialization
+ occurring. */
+ templates =
+ tree_cons (NULL_TREE, fndecl, templates);
+ }
+ }
+
+ if (templates)
+ /* This function might be an instantiation or a specialization.
+ We should verify that this is possible. If it is, we must
+ somehow add the new declaration to the method vector for the
+ class. Perhaps we should use add_method? For now, we simply
+ return NULL_TREE, which lets the caller know that this
+ function is new, but we don't print an error message. */
+ return NULL_TREE;
if (methods != end)
{
for (fnname = maybe_templates; fnname; fnname = TREE_CHAIN (fnname))
{
- tree *args, fn, decl = TREE_VALUE (fnname);
+ tree args, fn, decl = TREE_VALUE (fnname);
if (DECL_INITIAL (decl))
continue;
fn = TREE_PURPOSE (fnname);
args = get_bindings (fn, decl);
fn = instantiate_template (fn, args);
- free (args);
instantiate_decl (fn);
}
case TEMPLATE_DECL:
{
- tree args = DECL_TEMPLATE_PARMS (t);
- int i, len = args ? TREE_VEC_LENGTH (args) : 0;
- OB_PUTS ("template <");
- for (i = 0; i < len; i++)
+ tree orig_args = DECL_TEMPLATE_PARMS (t);
+ tree args;
+ int i;
+ for (args = orig_args = nreverse (orig_args);
+ args;
+ args = TREE_CHAIN (args))
{
- tree arg = TREE_VEC_ELT (args, i);
- tree defval = TREE_PURPOSE (arg);
- arg = TREE_VALUE (arg);
- if (TREE_CODE (arg) == TYPE_DECL)
- {
- OB_PUTS ("class ");
- OB_PUTID (DECL_NAME (arg));
- }
- else
- dump_decl (arg, 1);
+ int len = TREE_VEC_LENGTH (TREE_VALUE (args));
- if (defval)
+ OB_PUTS ("template <");
+ for (i = 0; i < len; i++)
{
- OB_PUTS (" = ");
- dump_decl (defval, 1);
- }
+ tree arg = TREE_VEC_ELT (TREE_VALUE (args), i);
+ tree defval = TREE_PURPOSE (arg);
+ arg = TREE_VALUE (arg);
+ if (TREE_CODE (arg) == TYPE_DECL)
+ {
+ OB_PUTS ("class ");
+ OB_PUTID (DECL_NAME (arg));
+ }
+ else
+ dump_decl (arg, 1);
+
+ if (defval)
+ {
+ OB_PUTS (" = ");
+ dump_decl (defval, 1);
+ }
- OB_PUTC2 (',', ' ');
+ OB_PUTC2 (',', ' ');
+ }
+ if (len != 0)
+ OB_UNPUT (2);
+ OB_PUTC2 ('>', ' ');
}
- if (len != 0)
- OB_UNPUT (2);
- OB_PUTC2 ('>', ' ');
+ nreverse(orig_args);
if (TREE_CODE (DECL_TEMPLATE_RESULT (t)) == TYPE_DECL)
dump_type (TREE_TYPE (t), v);
tree t;
int v;
{
- tree name = DECL_ASSEMBLER_NAME (t);
- tree fntype = TREE_TYPE (t);
- tree parmtypes = TYPE_ARG_TYPES (fntype);
+ tree name;
+ tree fntype;
+ tree parmtypes;
tree cname = NULL_TREE;
+ if (TREE_CODE (t) == TEMPLATE_DECL)
+ t = DECL_TEMPLATE_RESULT (t);
+
+ name = DECL_ASSEMBLER_NAME (t);
+ fntype = TREE_TYPE (t);
+ parmtypes = TYPE_ARG_TYPES (fntype);
+
/* Friends have DECL_CLASS_CONTEXT set, but not DECL_CONTEXT. */
if (DECL_CONTEXT (t))
cname = DECL_CLASS_CONTEXT (t);
case TEMPLATE_CONST_PARM:
if (current_template_parms)
{
- tree r = TREE_VEC_ELT (TREE_VALUE (current_template_parms),
- TEMPLATE_CONST_IDX (t));
+ int i;
+ tree parms;
+ tree r;
+
+ for (parms = current_template_parms;
+ TREE_CHAIN (parms);
+ parms = TREE_CHAIN (parms))
+ ;
+
+ r = TREE_VEC_ELT (TREE_VALUE (parms),
+ TEMPLATE_CONST_IDX (t));
dump_decl (TREE_VALUE (r), -1);
}
else
context = hack_decl_function_context (t->fndecl);
if (context)
push_cp_function_context (context);
+ if (is_member_template (t->fndecl))
+ begin_member_template_processing (DECL_TI_ARGS (t->fndecl));
if (t->len > 0)
{
feed_input (t->buf, t->len);
{
tree context;
struct pending_inline *i = (struct pending_inline *) TREE_PURPOSE (t);
- context = hack_decl_function_context (i->fndecl);
+ context = hack_decl_function_context (i->fndecl);
+ if (is_member_template (i->fndecl))
+ end_member_template_processing ();
if (context)
pop_cp_function_context (context);
i = i->next;
context = hack_decl_function_context (i->fndecl);
if (context)
push_cp_function_context (context);
+ if (is_member_template (i->fndecl))
+ begin_member_template_processing (DECL_TI_ARGS (i->fndecl));
feed_input (i->buf, i->len);
lineno = i->lineno;
input_filename = i->filename;
arglist = TREE_VALUE (template);
template = TREE_PURPOSE (template);
tname = DECL_NAME (template);
- parmlist = DECL_ARGUMENTS (template);
+ parmlist = DECL_INNERMOST_TEMPLATE_PARMS (template);
nparms = TREE_VEC_LENGTH (parmlist);
OB_PUTC ('t');
icat (IDENTIFIER_LENGTH (tname));
}
else
{
- parm = tsubst (parm, &TREE_VEC_ELT (arglist, 0),
+ parm = tsubst (parm, arglist,
TREE_VEC_LENGTH (arglist), NULL_TREE);
/* It's a PARM_DECL. */
build_overload_name (TREE_TYPE (parm), 0, 0);
$$ = start_method (specs, $2); goto rest_of_mdef; }
| constructor_declarator
{ $$ = start_method (NULL_TREE, $$); goto rest_of_mdef; }
+ | template_header fn.def2
+ {
+ end_template_decl ();
+ if ($2 && DECL_TEMPLATE_INFO ($2))
+ {
+ $$ = DECL_TI_TEMPLATE ($2);
+ check_member_template ($$);
+ }
+ else if ($2)
+ $$ = $2;
+ else
+ {
+ cp_error("invalid member template declaration");
+ $$ = NULL_TREE;
+ }
+ }
;
return_id:
build_tree_list ($3, NULL_TREE)); }
| using_decl
{ $$ = do_class_using_decl ($1); }
- ;
+ | template_header component_decl_1
+ {
+ end_template_decl ();
+ if ($2 && DECL_TEMPLATE_INFO ($2))
+ {
+ $$ = DECL_TI_TEMPLATE ($2);
+ check_member_template ($$);
+ }
+ else if ($2)
+ $$ = $2;
+ else
+ {
+ cp_error("invalid member template declaration");
+ $$ = NULL_TREE;
+ }
+ }
/* The case of exactly one component is handled directly by component_decl. */
/* ??? Huh? ^^^ */
static int list_eq PROTO((tree, tree));
static tree get_class_bindings PROTO((tree, tree, tree));
static tree coerce_template_parms PROTO((tree, tree, tree));
-static tree tsubst_enum PROTO((tree, tree *, int));
+static tree tsubst_enum PROTO((tree, tree, int));
+static tree add_to_template_args PROTO((tree, tree));
+
+/* Restore the template parameter context. */
+
+void
+begin_member_template_processing (parms)
+ tree parms;
+{
+ int i;
+
+ ++processing_template_decl;
+ current_template_parms
+ = tree_cons (build_int_2 (0, processing_template_decl),
+ parms, current_template_parms);
+ for (i = 0; i < TREE_VEC_LENGTH (parms); ++i)
+ {
+ tree parm = TREE_VEC_ELT (parms, i);
+
+ switch (TREE_CODE (parm))
+ {
+ case TEMPLATE_TYPE_PARM:
+ pushdecl (TYPE_NAME (parm));
+ break;
+
+ case TEMPLATE_CONST_PARM:
+ pushdecl (parm);
+ break;
+
+ default:
+ my_friendly_abort (0);
+ }
+ }
+}
+
+/* Undo the effects of begin_member_template_processing. */
+
+void
+end_member_template_processing ()
+{
+ if (! processing_template_decl)
+ return;
+
+ --processing_template_decl;
+ current_template_parms = TREE_CHAIN (current_template_parms);
+}
+
+/* Returns non-zero iff T is a member template function. Works if T
+ is either a FUNCTION_DECL or a TEMPLATE_DECL. */
+
+int
+is_member_template (t)
+ tree t;
+{
+ int r = 0;
+
+ if (DECL_FUNCTION_MEMBER_P (t) ||
+ (TREE_CODE (t) == TEMPLATE_DECL &&
+ DECL_FUNCTION_MEMBER_P (DECL_TEMPLATE_RESULT (t))))
+ {
+ tree tmpl = NULL_TREE;
+
+ if (DECL_FUNCTION_TEMPLATE_P (t))
+ tmpl = t;
+ else if (DECL_TEMPLATE_INFO (t)
+ && DECL_FUNCTION_TEMPLATE_P (DECL_TI_TEMPLATE (t)))
+ tmpl = DECL_TI_TEMPLATE (t);
+
+ if (tmpl)
+ {
+ tree parms = DECL_TEMPLATE_PARMS (tmpl);
+ int parm_levels = list_length (parms);
+ int template_class_levels = 0;
+ tree ctx = DECL_CLASS_CONTEXT (t);
+
+ if (CLASSTYPE_TEMPLATE_INFO (ctx))
+ {
+ tree args;
+
+ /* Here, we should really count the number of levels
+ deep ctx is, making sure not to count any levels that
+ are just specializations. Since there are no member
+ template classes yet, we don't have to do all that. */
+
+ if (!CLASSTYPE_TEMPLATE_SPECIALIZATION (ctx))
+ template_class_levels = 1;
+ else
+ {
+ int i;
+
+ args = CLASSTYPE_TI_ARGS (ctx);
+
+ if (args == NULL_TREE)
+ template_class_levels = 1;
+ else
+ for (i = 0; i < TREE_VEC_LENGTH (args); ++i)
+ if (uses_template_parms (TREE_VEC_ELT (args, i)))
+ {
+ template_class_levels++;
+ break;
+ }
+ }
+ }
+
+ if (parm_levels > template_class_levels)
+ r = 1;
+ }
+ }
+
+ return r;
+}
+
+/* Return a new template argument vector which contains all of ARGS,
+ but has as its innermost set of arguments the EXTRA_ARGS. */
+
+tree
+add_to_template_args (args, extra_args)
+ tree args;
+ tree extra_args;
+{
+ tree new_args;
+
+ if (TREE_CODE (TREE_VEC_ELT (args, 0)) != TREE_VEC)
+ {
+ new_args = make_tree_vec (2);
+ TREE_VEC_ELT (new_args, 0) = args;
+ }
+ else
+ {
+ int i;
+
+ new_args = make_tree_vec (TREE_VEC_LENGTH (args) - 1);
+
+ for (i = 0; i < TREE_VEC_LENGTH (args); ++i)
+ TREE_VEC_ELT (new_args, i) = TREE_VEC_ELT (args, i);
+ }
+
+ TREE_VEC_ELT (new_args,
+ TREE_VEC_LENGTH (new_args) - 1) = extra_args;
+
+ return new_args;
+}
/* We've got a template header coming up; push to a new level for storing
the parms. */
current_template_args ()
{
tree header = current_template_parms;
- tree args = NULL_TREE;
+ int length = list_length (header);
+ tree args = make_tree_vec (length);
+ int l = length;
+
while (header)
{
tree a = copy_node (TREE_VALUE (header));
TREE_TYPE (a) = NULL_TREE;
while (i--)
{
- tree t = TREE_VALUE (TREE_VEC_ELT (a, i));
- if (TREE_CODE (t) == TYPE_DECL)
- t = TREE_TYPE (t);
- else
- t = DECL_INITIAL (t);
+ tree t = TREE_VEC_ELT (a, i);
+
+ /* t will be a list if we are called from within a
+ begin/end_template_parm_list pair, but a vector directly
+ if within a begin/end_member_template_processing pair. */
+ if (TREE_CODE (t) == TREE_LIST)
+ {
+ t = TREE_VALUE (t);
+
+ if (TREE_CODE (t) == TYPE_DECL)
+ t = TREE_TYPE (t);
+ else
+ t = DECL_INITIAL (t);
+ }
+
TREE_VEC_ELT (a, i) = t;
}
- args = tree_cons (TREE_PURPOSE (header), a, args);
+ TREE_VEC_ELT (args, --l) = a;
header = TREE_CHAIN (header);
}
- args = nreverse (args);
-
- /* FIXME Remove this when we support member templates. */
- args = TREE_VALUE (args);
return args;
}
if (! ctx || TYPE_BEING_DEFINED (ctx))
{
tmpl = build_lang_decl (TEMPLATE_DECL, DECL_NAME (decl), NULL_TREE);
- DECL_TEMPLATE_PARMS (tmpl) = TREE_VALUE (current_template_parms);
+ DECL_TEMPLATE_PARMS (tmpl) = current_template_parms;
DECL_CONTEXT (tmpl) = DECL_CONTEXT (decl);
+ if (DECL_LANG_SPECIFIC (decl))
+ DECL_CLASS_CONTEXT (tmpl) = DECL_CLASS_CONTEXT (decl);
}
else
{
tree t;
+ tree a;
if (CLASSTYPE_TEMPLATE_INSTANTIATION (ctx))
cp_error ("must specialize `%#T' before defining member `%#D'",
}
else
tmpl = DECL_TI_TEMPLATE (decl);
+
+ if (is_member_template (tmpl))
+ {
+ a = TREE_VEC_ELT (args, TREE_VEC_LENGTH (args) - 1);
+ t = DECL_INNERMOST_TEMPLATE_PARMS (DECL_TI_TEMPLATE (decl));
+ if (TREE_VEC_LENGTH (t)
+ != TREE_VEC_LENGTH (a))
+ {
+ cp_error ("got %d template parameters for `%#D'",
+ TREE_VEC_LENGTH (a), decl);
+ cp_error (" but %d required", TREE_VEC_LENGTH (t));
+ }
+ if (TREE_VEC_LENGTH (args) > 1)
+ /* Get the template parameters for the enclosing template
+ class. */
+ a = TREE_VEC_ELT (args, TREE_VEC_LENGTH (args) - 2);
+ else
+ a = NULL_TREE;
+ }
+ else
+ a = TREE_VEC_ELT (args, TREE_VEC_LENGTH (args) - 1);
+
+ t = NULL_TREE;
if (CLASSTYPE_TEMPLATE_SPECIALIZATION (ctx))
- t = TREE_VALUE (CLASSTYPE_TI_SPEC_INFO (ctx));
- else
- t = DECL_TEMPLATE_PARMS (CLASSTYPE_TI_TEMPLATE (ctx));
+ {
+ /* When processing an inline member template of a
+ specialized class, there is no CLASSTYPE_TI_SPEC_INFO. */
+ if (CLASSTYPE_TI_SPEC_INFO (ctx))
+ t = TREE_VALUE (CLASSTYPE_TI_SPEC_INFO (ctx));
+ }
+ else if (CLASSTYPE_TEMPLATE_INFO (ctx))
+ t = DECL_INNERMOST_TEMPLATE_PARMS (CLASSTYPE_TI_TEMPLATE (ctx));
+
+ /* There should be template arguments if and only if there is a
+ template class. */
+ my_friendly_assert((a != NULL_TREE) == (t != NULL_TREE), 0);
- if (TREE_VEC_LENGTH (t) != TREE_VEC_LENGTH (args))
+ if (t != NULL_TREE
+ && TREE_VEC_LENGTH (t) != TREE_VEC_LENGTH (a))
{
cp_error ("got %d template parameters for `%#D'",
- TREE_VEC_LENGTH (args), decl);
+ TREE_VEC_LENGTH (a), decl);
cp_error (" but `%#T' has %d", ctx, TREE_VEC_LENGTH (t));
}
}
+ /* Get the innermost set of template arguments. */
+ args = TREE_VEC_ELT (args, TREE_VEC_LENGTH (args) - 1);
DECL_TEMPLATE_RESULT (tmpl) = decl;
TREE_TYPE (tmpl) = TREE_TYPE (decl);
tmpl = pushdecl_top_level (tmpl);
if (primary)
- TREE_TYPE (DECL_TEMPLATE_PARMS (tmpl)) = tmpl;
+ TREE_TYPE (DECL_INNERMOST_TEMPLATE_PARMS (tmpl)) = tmpl;
info = perm_tree_cons (tmpl, args, NULL_TREE);
DECL_TEMPLATE_INFO (decl) = info;
}
-tree tsubst PROTO ((tree, tree*, int, tree));
-
/* Convert all template arguments to their appropriate types, and return
a vector containing the resulting values. If any error occurs, return
error_mark_node. */
else if (TREE_CODE (TREE_VALUE (TREE_VEC_ELT (parms, i)))
== TYPE_DECL)
arg = tsubst (TREE_PURPOSE (TREE_VEC_ELT (parms, i)),
- &TREE_VEC_ELT (vec, 0), i, in_decl);
+ vec, i, in_decl);
else
arg = tsubst_expr (TREE_PURPOSE (TREE_VEC_ELT (parms, i)),
- &TREE_VEC_ELT (vec, 0), i, in_decl);
+ vec, i, in_decl);
TREE_VEC_ELT (vec, i) = arg;
}
}
else
{
- tree t = tsubst (TREE_TYPE (parm), &TREE_VEC_ELT (vec, 0),
+ tree t = tsubst (TREE_TYPE (parm), vec,
TREE_VEC_LENGTH (vec), in_decl);
if (processing_template_decl)
val = arg;
tree name = DECL_NAME (CLASSTYPE_TI_TEMPLATE (t));
char *mangled_name = mangle_class_name_for_template
(IDENTIFIER_POINTER (name),
- DECL_TEMPLATE_PARMS (CLASSTYPE_TI_TEMPLATE (t)),
+ DECL_INNERMOST_TEMPLATE_PARMS (CLASSTYPE_TI_TEMPLATE (t)),
CLASSTYPE_TI_ARGS (t));
tree id = get_identifier (mangled_name);
IDENTIFIER_TEMPLATE (id) = name;
if (PRIMARY_TEMPLATE_P (template))
{
- parmlist = DECL_TEMPLATE_PARMS (template);
+ parmlist = DECL_INNERMOST_TEMPLATE_PARMS (template);
arglist = coerce_template_parms (parmlist, arglist, template);
if (arglist == error_mark_node)
tree elt;
TREE_VEC_ELT (bases, i) = elt
- = tsubst (TREE_VEC_ELT (pbases, i), &TREE_VEC_ELT (args, 0),
+ = tsubst (TREE_VEC_ELT (pbases, i), args,
TREE_VEC_LENGTH (args), NULL_TREE);
BINFO_INHERITANCE_CHAIN (elt) = binfo;
/* These will add themselves to CLASSTYPE_TAGS for the new type. */
if (TREE_CODE (tag) == ENUMERAL_TYPE)
{
- tree e, newtag = tsubst_enum (tag, &TREE_VEC_ELT (args, 0),
+ tree e, newtag = tsubst_enum (tag, args,
TREE_VEC_LENGTH (args));
*field_chain = grok_enum_decls (newtag, NULL_TREE);
}
}
else
- tsubst (tag, &TREE_VEC_ELT (args, 0),
+ tsubst (tag, args,
TREE_VEC_LENGTH (args), NULL_TREE);
}
for (t = TYPE_FIELDS (pattern); t; t = TREE_CHAIN (t))
if (TREE_CODE (t) != CONST_DECL)
{
- tree r = tsubst (t, &TREE_VEC_ELT (args, 0),
+ tree r = tsubst (t, args,
TREE_VEC_LENGTH (args), NULL_TREE);
if (TREE_CODE (r) == VAR_DECL)
{
DECL_FRIENDLIST (TYPE_MAIN_DECL (type))
= tsubst (DECL_FRIENDLIST (TYPE_MAIN_DECL (pattern)),
- &TREE_VEC_ELT (args, 0), TREE_VEC_LENGTH (args), NULL_TREE);
+ args, TREE_VEC_LENGTH (args), NULL_TREE);
{
tree d = CLASSTYPE_FRIEND_CLASSES (type)
- = tsubst (CLASSTYPE_FRIEND_CLASSES (pattern), &TREE_VEC_ELT (args, 0),
+ = tsubst (CLASSTYPE_FRIEND_CLASSES (pattern), args,
TREE_VEC_LENGTH (args), NULL_TREE);
/* This does injection for friend classes. */
for (; d; d = TREE_CHAIN (d))
TREE_VALUE (d) = xref_tag_from_type (TREE_VALUE (d), NULL_TREE, 1);
- d = tsubst (DECL_TEMPLATE_INJECT (template), &TREE_VEC_ELT (args, 0),
+ d = tsubst (DECL_TEMPLATE_INJECT (template), args,
TREE_VEC_LENGTH (args), NULL_TREE);
for (; d; d = TREE_CHAIN (d))
tree
tsubst (t, args, nargs, in_decl)
- tree t, *args;
+ tree t, args;
int nargs;
tree in_decl;
{
}
case TEMPLATE_TYPE_PARM:
+ case TEMPLATE_CONST_PARM:
{
- tree arg = args[TEMPLATE_TYPE_IDX (t)];
- return cp_build_type_variant
- (arg, TYPE_READONLY (arg) || TYPE_READONLY (t),
- TYPE_VOLATILE (arg) || TYPE_VOLATILE (t));
+ int idx;
+ int level;
+
+ if (TREE_CODE (t) == TEMPLATE_TYPE_PARM)
+ {
+ idx = TEMPLATE_TYPE_IDX (t);
+ level = TEMPLATE_TYPE_LEVEL (t);
+ }
+ else
+ {
+ idx = TEMPLATE_CONST_IDX (t);
+ level = TEMPLATE_CONST_LEVEL (t);
+ }
+
+ if (TREE_VEC_LENGTH (args) > 0)
+ {
+ tree arg = NULL_TREE;
+
+ if (TREE_CODE (TREE_VEC_ELT (args, 0)) == TREE_VEC)
+ {
+ if (TREE_VEC_LENGTH (args) >= level - 1)
+ arg = TREE_VEC_ELT
+ (TREE_VEC_ELT (args, level - 1), idx);
+ }
+ else if (level == 1)
+ arg = TREE_VEC_ELT (args, idx);
+
+ if (arg != NULL_TREE)
+ {
+ if (TREE_CODE (t) == TEMPLATE_TYPE_PARM)
+ return cp_build_type_variant
+ (arg, TYPE_READONLY (arg) || TYPE_READONLY (t),
+ TYPE_VOLATILE (arg) || TYPE_VOLATILE (t));
+ else
+ return arg;
+ }
+ }
+
+ /* If we get here, we must have been looking at a parm for a
+ more deeply nested template. */
+ my_friendly_assert((TREE_CODE (t) == TEMPLATE_CONST_PARM
+ && TEMPLATE_CONST_LEVEL (t) > 1)
+ || (TREE_CODE (t) == TEMPLATE_TYPE_PARM
+ && TEMPLATE_TYPE_LEVEL (t) > 1),
+ 0);
+ return t;
}
- case TEMPLATE_CONST_PARM:
- return args[TEMPLATE_CONST_IDX (t)];
+ case TEMPLATE_DECL:
+ {
+ /* We can get here when processing a member template function
+ of a template class. */
+ tree tmpl;
+ tree decl = DECL_TEMPLATE_RESULT (t);
+ tree new_decl;
+ tree parms;
+ int i;
+
+ /* We might already have an instance of this template. */
+ tree instances = DECL_TEMPLATE_INSTANTIATIONS (t);
+ tree ctx = tsubst (DECL_CLASS_CONTEXT (t), args, nargs, in_decl);
+
+ for (; instances; instances = TREE_CHAIN (instances))
+ if (DECL_CLASS_CONTEXT (TREE_VALUE (instances)) == ctx)
+ return TREE_VALUE (instances);
+
+ /* Make a new template decl. It will be similar to the
+ original, but will record the current template arguments.
+ We also create a new function declaration, which is just
+ like the old one, but points to this new template, rather
+ than the old one. */
+ tmpl = copy_node (t);
+ copy_lang_decl (tmpl);
+ my_friendly_assert (DECL_LANG_SPECIFIC (tmpl) != 0, 0);
+ DECL_CHAIN (tmpl) = NULL_TREE;
+ TREE_CHAIN (tmpl) = NULL_TREE;
+ DECL_TEMPLATE_INFO (tmpl) = build_tree_list (t, args);
+ new_decl = tsubst (decl, args, nargs, in_decl);
+ DECL_RESULT (tmpl) = new_decl;
+ DECL_INITIAL (new_decl) = DECL_INITIAL (decl);
+ DECL_TI_TEMPLATE (new_decl) = tmpl;
+ TREE_TYPE (tmpl) = TREE_TYPE (new_decl);
+ DECL_TEMPLATE_INSTANTIATIONS(tmpl) = NULL_TREE;
+
+ /* The template parameters for this new template are all the
+ template parameters for the old template, except the
+ outermost level of parameters. */
+ DECL_TEMPLATE_PARMS (tmpl)
+ = copy_node (DECL_TEMPLATE_PARMS (tmpl));
+ for (parms = DECL_TEMPLATE_PARMS (tmpl);
+ TREE_CHAIN (parms) != NULL_TREE;
+ parms = TREE_CHAIN (parms))
+ TREE_CHAIN (parms) = copy_node (TREE_CHAIN (parms));
+
+ /* Record this partial instantiation. */
+ DECL_TEMPLATE_INSTANTIATIONS (t)
+ = perm_tree_cons (NULL_TREE, tmpl,
+ DECL_TEMPLATE_INSTANTIATIONS (t));
+ return tmpl;
+ }
case FUNCTION_DECL:
{
/* Do we already have this instantiation? */
if (DECL_TEMPLATE_INFO (t) != NULL_TREE)
{
- tree tmpl = TREE_PURPOSE (DECL_TEMPLATE_INFO (t));
+ tree tmpl = DECL_TI_TEMPLATE (t);
tree decls = DECL_TEMPLATE_INSTANTIATIONS (tmpl);
for (; decls; decls = TREE_CHAIN (decls))
{
tree tmpl = DECL_TI_TEMPLATE (t);
tree *declsp = &DECL_TEMPLATE_INSTANTIATIONS (tmpl);
- tree argvec = tsubst (TREE_VALUE (DECL_TEMPLATE_INFO (t)),
- args, nargs, in_decl);
+ tree argvec = tsubst (DECL_TI_ARGS (t), args, nargs, in_decl);
+
+ if (DECL_TEMPLATE_INFO (tmpl) && DECL_TI_ARGS (tmpl))
+ argvec = add_to_template_args (DECL_TI_ARGS (tmpl), argvec);
DECL_TEMPLATE_INFO (r) = perm_tree_cons (tmpl, argvec, NULL_TREE);
*declsp = perm_tree_cons (argvec, r, *declsp);
{
tree tmpl = DECL_TI_TEMPLATE (t);
tree *declsp = &DECL_TEMPLATE_INSTANTIATIONS (tmpl);
- tree argvec = tsubst (TREE_VALUE (DECL_TEMPLATE_INFO (t)),
- args, nargs, in_decl);
+ tree argvec = tsubst (DECL_TI_ARGS (t), args, nargs, in_decl);
DECL_TEMPLATE_INFO (r) = perm_tree_cons (tmpl, argvec, NULL_TREE);
*declsp = perm_tree_cons (argvec, r, *declsp);
tree
tsubst_copy (t, args, nargs, in_decl)
- tree t, *args;
+ tree t, args;
int nargs;
tree in_decl;
{
mark_used (t);
return t;
+ case TEMPLATE_DECL:
+ if (is_member_template (t))
+ return tsubst (t, args, nargs, in_decl);
+ else
+ return t;
+
#if 0
case IDENTIFIER_NODE:
return do_identifier (t, 0);
tree
tsubst_expr (t, args, nargs, in_decl)
- tree t, *args;
+ tree t, args;
int nargs;
tree in_decl;
{
tree
instantiate_template (tmpl, targ_ptr)
- tree tmpl, *targ_ptr;
+ tree tmpl, targ_ptr;
{
tree fndecl;
int i, len;
function_maybepermanent_obstack = &permanent_obstack;
my_friendly_assert (TREE_CODE (tmpl) == TEMPLATE_DECL, 283);
- len = TREE_VEC_LENGTH (DECL_TEMPLATE_PARMS (tmpl));
+ len = DECL_NTPARMS (tmpl);
i = len;
while (i--)
{
- tree t = targ_ptr [i];
+ tree t = TREE_VEC_ELT (targ_ptr, i);
if (TREE_CODE_CLASS (TREE_CODE (t)) == 't')
{
tree nt = target_type (t);
goto out;
}
}
- targ_ptr[i] = copy_to_permanent (t);
+ TREE_VEC_ELT (targ_ptr, i) = copy_to_permanent (t);
}
+ if (DECL_TEMPLATE_INFO (tmpl) && DECL_TI_ARGS (tmpl))
+ targ_ptr = add_to_template_args (DECL_TI_ARGS (tmpl), targ_ptr);
+
/* substitute template parameters */
fndecl = tsubst (DECL_RESULT (tmpl), targ_ptr, len, tmpl);
pushdecl_class_level (decl);
}
+/* Like type_unfication but designed specially to handle conversion
+ operators. */
+
+int
+fn_type_unification (fn, targs, args, return_type, strict)
+ tree fn, targs, args, return_type;
+ int strict;
+{
+ int i, dummy = 0;
+ tree fn_arg_types = TYPE_ARG_TYPES (TREE_TYPE (fn));
+ tree decl_arg_types = args;
+
+ my_friendly_assert (TREE_CODE (fn) == TEMPLATE_DECL, 0);
+
+ if (IDENTIFIER_TYPENAME_P (DECL_NAME (fn)))
+ {
+ /* This is a template conversion operator. Use the return types
+ as well as the argument types. */
+ fn_arg_types = tree_cons (NULL_TREE,
+ TREE_TYPE (TREE_TYPE (fn)),
+ fn_arg_types);
+ decl_arg_types = tree_cons (NULL_TREE,
+ return_type,
+ decl_arg_types);
+ }
+
+ i = type_unification (DECL_INNERMOST_TEMPLATE_PARMS (fn),
+ &TREE_VEC_ELT (targs, 0),
+ fn_arg_types,
+ decl_arg_types,
+ &dummy, 0, strict);
+
+ return i;
+}
+
+
/* Type unification.
We have a function template signature with one or more references to
/* Have to back unify here */
arg = TREE_VALUE (arg);
nsubsts = 0;
- ntparms = TREE_VEC_LENGTH (DECL_TEMPLATE_PARMS (arg));
+ ntparms = DECL_NTPARMS (arg);
targs = (tree *) alloca (sizeof (tree) * ntparms);
parm = tree_cons (NULL_TREE, parm, NULL_TREE);
- return type_unification (DECL_TEMPLATE_PARMS (arg), targs,
+ return type_unification (DECL_INNERMOST_TEMPLATE_PARMS (arg),
+ targs,
TYPE_ARG_TYPES (TREE_TYPE (arg)),
parm, &nsubsts, 0, strict);
}
more_specialized (pat1, pat2)
tree pat1, pat2;
{
- tree *targs;
+ tree targs;
int winner = 0;
targs = get_bindings (pat1, pat2);
if (targs)
{
- free (targs);
--winner;
}
targs = get_bindings (pat2, pat1);
if (targs)
{
- free (targs);
++winner;
}
/* Return the template arguments that will produce the function signature
DECL from the function template FN. */
-tree *
+tree
get_bindings (fn, decl)
tree fn, decl;
{
- int ntparms = TREE_VEC_LENGTH (DECL_TEMPLATE_PARMS (fn));
- tree *targs = (tree *) malloc (sizeof (tree) * ntparms);
- int i, dummy = 0;
- i = type_unification (DECL_TEMPLATE_PARMS (fn), targs,
- TYPE_ARG_TYPES (TREE_TYPE (fn)),
- TYPE_ARG_TYPES (TREE_TYPE (decl)),
- &dummy, 0, 1);
+ int ntparms = DECL_NTPARMS (fn);
+ tree targs = make_tree_vec (ntparms);
+ int i;
+
+ i = fn_type_unification (fn, targs,
+ TYPE_ARG_TYPES (TREE_TYPE (decl)),
+ TREE_TYPE (TREE_TYPE (decl)),
+ 1);
+
if (i == 0)
return targs;
- free (targs);
return 0;
}
most_specialized (fns, decl)
tree fns, decl;
{
- tree fn, champ, *args, *p;
+ tree fn, champ, args, *p;
int fate;
for (p = &fns; *p; )
args = get_bindings (TREE_VALUE (*p), decl);
if (args)
{
- free (args);
p = &TREE_CHAIN (*p);
}
else
tree fn;
tree result = NULL_TREE;
int extern_p = 0;
+ tree templates = NULL_TREE;
if (! DECL_LANG_SPECIFIC (decl))
{
fn = IDENTIFIER_GLOBAL_VALUE (name),
fn && DECL_TEMPLATE_INSTANTIATION (fn))
result = fn;
+ else
+ {
+ /* Maybe this is an instantiation of a member template
+ function. */
+ tree ctype = DECL_CONTEXT (decl);
+
+ name = DECL_NAME (decl);
+ fn = lookup_fnfields (TYPE_BINFO (ctype), name, 1);
+ if (fn)
+ fn = TREE_VALUE (fn);
+
+ for (; fn; fn = DECL_CHAIN (fn))
+ if (decls_match (fn, decl) && DECL_DEFER_OUTPUT (fn))
+ {
+ result = fn;
+ break;
+ }
+ else if (TREE_CODE (fn) == TEMPLATE_DECL)
+ templates = decl_tree_cons (NULL_TREE, fn, templates);
+ }
}
else if (name = DECL_NAME (decl), fn = IDENTIFIER_GLOBAL_VALUE (name), fn)
{
- tree templates = NULL_TREE;
for (fn = get_first_fn (fn); fn; fn = DECL_CHAIN (fn))
- if (decls_match (fn, decl)
- && DECL_DEFER_OUTPUT (fn))
+ if (decls_match (fn, decl) && DECL_DEFER_OUTPUT (fn))
{
result = fn;
break;
}
else if (TREE_CODE (fn) == TEMPLATE_DECL)
templates = decl_tree_cons (NULL_TREE, fn, templates);
+ }
- if (! result)
+ if (templates && !result)
+ {
+ tree args;
+ result = most_specialized (templates, decl);
+ if (result == error_mark_node)
{
- tree *args;
- result = most_specialized (templates, decl);
- if (result == error_mark_node)
+ char *str = "candidates are:";
+ cp_error ("ambiguous template instantiation for `%D' requested", decl);
+ for (fn = templates; fn; fn = TREE_CHAIN (fn))
{
- char *str = "candidates are:";
- cp_error ("ambiguous template instantiation for `%D' requested", decl);
- for (fn = templates; fn; fn = TREE_CHAIN (fn))
- {
- cp_error_at ("%s %+#D", str, TREE_VALUE (fn));
- str = " ";
- }
- return;
- }
- else if (result)
- {
- args = get_bindings (result, decl);
- result = instantiate_template (result, args);
- free (args);
+ cp_error_at ("%s %+#D", str, TREE_VALUE (fn));
+ str = " ";
}
+ return;
+ }
+ else if (result)
+ {
+ args = get_bindings (result, decl);
+ result = instantiate_template (result, args);
}
}
+
if (! result)
{
cp_error ("no matching template for `%D' found", decl);
{
pushclass (DECL_CONTEXT (d), 2);
DECL_INITIAL (d) = tsubst_expr
- (DECL_INITIAL (pattern), &TREE_VEC_ELT (args, 0),
+ (DECL_INITIAL (pattern), args,
TREE_VEC_LENGTH (args), tmpl);
popclass (1);
}
/* Trick tsubst into giving us a new decl in case the template changed. */
save_ti = DECL_TEMPLATE_INFO (pattern);
DECL_TEMPLATE_INFO (pattern) = NULL_TREE;
- td = tsubst (pattern, &TREE_VEC_ELT (args, 0), TREE_VEC_LENGTH (args), tmpl);
+ td = tsubst (pattern, args, TREE_VEC_LENGTH (args), tmpl);
DECL_TEMPLATE_INFO (pattern) = save_ti;
/* And set up DECL_INITIAL, since tsubst doesn't. */
{
pushclass (DECL_CONTEXT (d), 2);
DECL_INITIAL (td) = tsubst_expr
- (DECL_INITIAL (pattern), &TREE_VEC_ELT (args, 0),
+ (DECL_INITIAL (pattern), args,
TREE_VEC_LENGTH (args), tmpl);
popclass (1);
}
{
store_return_init
(TREE_OPERAND (t, 0),
- tsubst_expr (TREE_OPERAND (t, 1), &TREE_VEC_ELT (args, 0),
+ tsubst_expr (TREE_OPERAND (t, 1), args,
TREE_VEC_LENGTH (args), tmpl));
t = TREE_CHAIN (t);
}
keep_next_level ();
my_friendly_assert (TREE_CODE (t) == COMPOUND_STMT, 42);
- tsubst_expr (t, &TREE_VEC_ELT (args, 0),
- TREE_VEC_LENGTH (args), tmpl);
+ tsubst_expr (t, args, TREE_VEC_LENGTH (args), tmpl);
finish_function (lineno, 0, nested);
}
{
if (t)
{
- tree first = tsubst (t, &TREE_VEC_ELT (argvec, 0),
+ tree first = tsubst (t, argvec,
TREE_VEC_LENGTH (argvec), NULL_TREE);
tree last = first;
for (t = TREE_CHAIN (t); t; t = TREE_CHAIN (t))
{
- tree x = tsubst (t, &TREE_VEC_ELT (argvec, 0),
- TREE_VEC_LENGTH (argvec), NULL_TREE);
+ tree x = tsubst (t, argvec, TREE_VEC_LENGTH (argvec), NULL_TREE);
TREE_CHAIN (last) = x;
last = x;
}
for (; t; t = TREE_CHAIN (t))
{
- tree pur = tsubst_copy (TREE_PURPOSE (t), &TREE_VEC_ELT (argvec, 0),
+ tree pur = tsubst_copy (TREE_PURPOSE (t), argvec,
TREE_VEC_LENGTH (argvec), NULL_TREE);
- tree val = tsubst_expr (TREE_VALUE (t), &TREE_VEC_ELT (argvec, 0),
+ tree val = tsubst_expr (TREE_VALUE (t), argvec,
TREE_VEC_LENGTH (argvec), NULL_TREE);
*p = build_tree_list (pur, val);
p = &TREE_CHAIN (*p);
static tree
tsubst_enum (tag, args, nargs)
- tree tag, *args;
+ tree tag, args;
int nargs;
{
tree newtag = start_enum (TYPE_IDENTIFIER (tag));
if (DECL_NAME (*methods) == name)
break;
}
+
+ /* If we didn't find it, it might have been a template
+ conversion operator. (Note that we don't look for this case
+ above so that we will always find specializations first.) */
+ if (methods == end
+ && IDENTIFIER_TYPENAME_P (name))
+ {
+ methods = &TREE_VEC_ELT (method_vec, 0) + 1;
+
+ while (++methods != end)
+ {
+ if (TREE_CODE (*methods) == TEMPLATE_DECL
+ && IDENTIFIER_TYPENAME_P (DECL_NAME (*methods)))
+ break;
+ }
+ }
+
if (methods != end)
return methods - &TREE_VEC_ELT (method_vec, 0);
}
return 0;
case TEMPLATE_CONST_PARM:
- return TEMPLATE_CONST_IDX (t1) == TEMPLATE_CONST_IDX (t2);
+ return TEMPLATE_CONST_IDX (t1) == TEMPLATE_CONST_IDX (t2)
+ && TEMPLATE_CONST_LEVEL (t1) == TEMPLATE_CONST_LEVEL (t2);
case SIZEOF_EXPR:
if (TREE_CODE (TREE_OPERAND (t1, 0)) != TREE_CODE (TREE_OPERAND (t2, 0)))
break;
case TEMPLATE_TYPE_PARM:
- return TEMPLATE_TYPE_IDX (t1) == TEMPLATE_TYPE_IDX (t2);
+ return TEMPLATE_TYPE_IDX (t1) == TEMPLATE_TYPE_IDX (t2)
+ && TEMPLATE_TYPE_LEVEL (t1) == TEMPLATE_TYPE_LEVEL (t2);
case TYPENAME_TYPE:
if (TYPE_IDENTIFIER (t1) != TYPE_IDENTIFIER (t2))
{
tree basetype = NULL_TREE;
- if (TREE_CODE (function) == FUNCTION_DECL)
+ if (TREE_CODE (function) == FUNCTION_DECL
+ || DECL_FUNCTION_TEMPLATE_P (function))
{
basetype = DECL_CLASS_CONTEXT (function);