home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Education Sampler 1992 [NeXTSTEP]
/
Education_1992_Sampler.iso
/
NeXT
/
GnuSource
/
cc-61.0.1
/
cc
/
cplus-pt.c
< prev
next >
Wrap
C/C++ Source or Header
|
1991-06-03
|
20KB
|
735 lines
/* Handle parameterized types (templates) for GNU C++.
Written by Ken Raeburn of Watchmaker Computing.
Copyright (C) 1991 Free Software Foundation, Inc.
This file is part of GNU CC.
GNU CC is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2, or (at your option)
any later version.
GNU CC is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with GNU CC; see the file COPYING. If not, write to
the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
#include <stdio.h>
#include <assert.h>
#include "obstack.h"
#include "config.h"
#include "tree.h"
#include "cplus-tree.h"
#include "cplus-decl.h"
#include "cplus-tab.h"
extern struct obstack permanent_obstack;
extern tree grokdeclarator ();
/* Someday, if/when our source language includes templates (yeah, right!),
make a class of "template_info" objects, and a class template for
general symbol table manipulation. */
struct template_info {
struct template_info *next;
tree parm_vec;
char *text;
int length;
char *filename;
int lineno;
};
static struct template_info *templates;
static struct template_info *current_template_info;
extern int lineno;
extern char *input_filename;
#define obstack_chunk_alloc xmalloc
#define obstack_chunk_free free
extern int xmalloc ();
extern void free ();
void overload_template_name ();
/* We've got a template header coming up; set obstacks up to save the
nodes created permanently. (There might be cases with nested templates
where we don't have to do this, but they aren't implemented, and it
probably wouldn't be worth the effort.) */
void
begin_template_parm_list ()
{
pushlevel (0);
push_obstacks (&permanent_obstack, &permanent_obstack);
}
/* The end of a template parameter list has been reached. Process the
tree list into a parameter vector, converting each parameter into a more
useful form. Type parameters are saved as IDENTIFIER_NODEs, and others
as PARM_DECLs.
For the moment, dump out a brief description and tell the user we aren't
doing anything useful with it. */
tree
end_template_parm_list (parms)
tree parms;
{
int nparms = 0;
tree saved_parmlist;
tree parm;
for (parm = parms; parm; parm = TREE_CHAIN (parm))
nparms++;
saved_parmlist = make_tree_vec (nparms);
for (parm = parms, nparms = 0; parm; parm = TREE_CHAIN (parm), nparms++)
{
tree p = parm;
assert (TREE_CODE (p) == TREE_LIST);
p = TREE_PURPOSE (p);
if (TREE_CODE (p) == IDENTIFIER_NODE)
{
/* We've got "class X"; extract the identifier name. */
/* TREE_VALUE (parm) is the base class, if any; ignore it for
now. */
}
else
{
assert (TREE_CODE (p) == TREE_LIST);
p = TREE_VALUE (p);
/* PARM is a variable declaration. Its TREE_VALUE contains the
identifier, with indirection and function calls and such wrapped
around it, and its TREE_PURPOSE contains the base type. Call
grokdeclarator to rearrange it into a proper VAR_DECL. */
p = grokdeclarator (TREE_VALUE (parm), TREE_PURPOSE (parm),
NORMAL, 0, NULL_TREE);
/* A template parameter is not modifiable. */
TREE_READONLY (p) = 1;
}
TREE_VEC_ELT (saved_parmlist, nparms) = p;
}
return saved_parmlist;
}
/* Process information from new template parameter NEXT and append it to the
LIST being built. The rules for use of a template parameter type name
by later parameters are not well-defined for us just yet. However, the
only way to avoid having to parse expressions of unknown complexity (and
with tokens of unknown types) is to disallow it completely. So for now,
that is what is assumed. */
tree
process_template_parm (list, next)
tree list, next;
{
tree parm;
int is_type;
parm = next;
assert (TREE_CODE (parm) == TREE_LIST);
is_type = TREE_CODE (TREE_PURPOSE (parm)) == IDENTIFIER_NODE;
if (is_type)
{
/* is a type */
}
else
{
parm = TREE_PURPOSE (parm);
assert (TREE_CODE (parm) == TREE_LIST);
parm = TREE_VALUE (parm);
/* is a const-param */
parm = grokdeclarator (TREE_VALUE (next), TREE_PURPOSE (next),
NORMAL, 0, NULL_TREE);
/* A template parameter is not modifiable. */
TREE_READONLY (parm) = 1;
}
return chainon (list, next);
}
/* end_template_decl is called after a template declaration is seen. */
/* D1 is template header; D2 is class_head_sans_basetype. */
void
end_template_decl (d1, d2)
tree d1, d2;
{
tree decl;
struct template_info *tmpl;
tmpl = (struct template_info *) obstack_alloc (&permanent_obstack,
sizeof (struct template_info));
tmpl->text = 0;
tmpl->length = 0;
/* tmpl->is_class = 1; /* XXX */
/* tmpl->name = 0; /* XXX */
templates = tmpl;
current_template_info = tmpl;
decl = make_node (TEMPLATE_DECL);
DECL_NAME (decl) = d2;
DECL_LANG_SPECIFIC (decl) = (struct lang_decl *) current_template_info;
DECL_ARGUMENTS (decl) = d1;
poplevel (0, 0, 0);
pushdecl (decl);
pop_obstacks ();
}
/* Convert all template arguments to their appropriate types, and return
a vector containing the resulting values. If any error occurs, return
error_mark_node. */
static tree
coerce_template_parms (parms, arglist)
tree parms, arglist;
{
int nparms, i, lost = 0;
tree vec;
nparms = list_length (arglist);
if (nparms != TREE_VEC_LENGTH (parms))
{
error ("incorrect number of parameters (%d, should be %d) in template expansion",
nparms, TREE_VEC_LENGTH (parms));
return error_mark_node;
}
vec = make_tree_vec (nparms);
for (i = 0; i < nparms; i++)
{
tree arg = arglist;
tree parm = TREE_VEC_ELT (parms, i);
tree val;
int is_type, requires_type;
if (arg == error_mark_node)
{
lose:
TREE_VEC_ELT (vec, i) = error_mark_node;
lost++;
continue;
}
arglist = TREE_CHAIN (arglist);
arg = TREE_VALUE (arg);
is_type = TREE_CODE_CLASS (TREE_CODE (arg)) == 't';
requires_type = TREE_CODE (parm) == IDENTIFIER_NODE;
if (is_type != requires_type)
{
error ("type/value mismatch in template parameter list");
goto lose;
}
if (is_type)
{
val = groktypename (arg);
}
else
{
val = digest_init (TREE_TYPE (parm), arg, (tree *) 0);
}
if (val == error_mark_node)
lost++;
TREE_VEC_ELT (vec, i) = val;
}
if (lost)
return error_mark_node;
return vec;
}
/* Given class template name and parameter list, produce a user-friendly name
for the instantiation. Note that this name isn't necessarily valid as
input to the compiler, because ">" characters may be adjacent. */
static char *
mangle_class_name_for_template (name, parms, arglist)
char *name;
tree parms, arglist;
{
extern char *build_overload_name (/* parm, text, text_end */);
static char buf[BUFSIZ];
int i, nparms;
char *bufp = buf, ibuf[40];
int icat_tmp;
#define buflen sizeof(buf)
#define check if (bufp >= buf+buflen-1) goto too_long
#define ccat(c) *bufp++=(c); check
#define advance bufp+=strlen(bufp); check
#define cat(s) strncpy(bufp, s, buf+buflen-bufp-1); advance
#define icat(n) sprintf(ibuf,"%d",(n)); cat(ibuf)
#define xcat(n) sprintf(ibuf,"%ux",n); cat(ibuf)
strcpy (bufp, name);
advance;
ccat ('<');
nparms = TREE_VEC_LENGTH (parms);
assert (nparms == TREE_VEC_LENGTH (arglist));
for (i = 0; i < nparms; i++)
{
tree parm = TREE_VEC_ELT (parms, i), arg = TREE_VEC_ELT (arglist, i);
if (i)
ccat (',');
switch (TREE_CODE (parm))
{
case IDENTIFIER_NODE:
{
/* parm is a type */
extern char * type_as_string ();
char *typename;
if (TYPE_NAME (arg)
&& DECL_NAME (TYPE_NAME (arg))
&& IDENTIFIER_POINTER (DECL_NAME (TYPE_NAME (arg))))
typename = IDENTIFIER_POINTER (DECL_NAME (TYPE_NAME (arg)));
else
typename = type_as_string (arg);
cat (typename);
break;
}
case PARM_DECL:
{
/* Should do conversions as for "const" initializers. */
tree type = TREE_TYPE (parm);
tree id = DECL_NAME (parm);
if (TREE_CODE (arg) == TREE_LIST)
{
/* New list cell was built because old chain link was in
use. */
assert (TREE_PURPOSE (arg) == NULL_TREE);
arg = TREE_VALUE (arg);
}
switch (TREE_CODE (type))
{
case INTEGER_TYPE:
if (TREE_CODE (arg) == INTEGER_CST)
{
if (TREE_INT_CST_HIGH (arg) != (((int)TREE_INT_CST_LOW (arg)) >> (HOST_BITS_PER_INT - 1)))
{
tree val = arg;
if (TREE_INT_CST_HIGH (val) < 0)
{
ccat ('-');
val = build_int_2 (~TREE_INT_CST_LOW (val),
-TREE_INT_CST_HIGH (val));
}
/* Would "%x%0*x" or "%x%*0x" get zero-padding on all
systems? Shouldn't hard-code "8" here. */
sprintf (ibuf, "%x%08x", TREE_INT_CST_HIGH (val),
TREE_INT_CST_LOW (val));
cat (ibuf);
}
else
icat (TREE_INT_CST_LOW (arg));
}
else
{
error ("invalid integer constant for template parameter");
cat ("*error*");
}
break;
default:
abort ();
}
}
break;
default:
abort ();
}
}
ccat ('>');
return buf;
too_long:
fatal ("out of (preallocated) string space creating template instantiation name");
}
/* Given an IDENTIFIER_NODE (type TEMPLATE_DECL) and a chain of
parameters, find the desired type.
D1 is the PTYPENAME terminal, and ARGLIST is the list of arguments.
Since ARGLIST is build on the decl_obstack, we must copy it here
to keep it from being reclaimed when the decl storage is reclaimed. */
tree
lookup_template_class (d1, arglist)
tree d1, arglist;
{
tree template, parmlist;
char *mangled_name;
tree id;
assert (TREE_CODE (d1) == IDENTIFIER_NODE);
template = IDENTIFIER_GLOBAL_VALUE (d1); /* XXX */
assert (template != NULL_TREE);
assert (TREE_CODE (template) == TEMPLATE_DECL);
parmlist = DECL_ARGUMENTS (template);
arglist = coerce_template_parms (parmlist, arglist);
if (arglist == error_mark_node)
return arglist;
mangled_name = mangle_class_name_for_template (IDENTIFIER_POINTER (d1),
parmlist, arglist);
id = get_identifier (mangled_name);
if (!IDENTIFIER_TEMPLATE (id))
{
arglist = copy_to_permanent (arglist);
IDENTIFIER_TEMPLATE (id) = perm_tree_cons (template, arglist, NULL_TREE);
}
return id;
}
void
push_template_decls (template, class_level)
tree template;
int class_level;
{
tree parmlist, arglist;
int i, nparms;
assert (template != 0);
assert (TREE_CODE (template) == TREE_LIST);
arglist = TREE_VALUE (template);
if (arglist == error_mark_node)
return;
assert (TREE_CODE (arglist) == TREE_VEC);
template = TREE_PURPOSE (template);
assert (TREE_CODE (template) == TEMPLATE_DECL);
parmlist = DECL_ARGUMENTS (template);
assert (TREE_CODE (parmlist) == TREE_VEC);
nparms = TREE_VEC_LENGTH (parmlist);
assert (DECL_NAME (template) != 0);
for (i = 0; i < nparms; i++)
{
int requires_type, is_type;
tree parm = TREE_VEC_ELT (parmlist, i);
tree arg = TREE_VEC_ELT (arglist, i);
tree decl = 0;
requires_type = TREE_CODE (parm) == IDENTIFIER_NODE;
is_type = TREE_CODE_CLASS (TREE_CODE (arg)) == 't';
if (is_type)
{
/* add typename to namespace */
if (!requires_type)
{
error ("template use error: type provided where value needed");
continue;
}
decl = arg;
assert (TREE_CODE_CLASS (TREE_CODE (decl)) == 't');
decl = build_decl (TYPE_DECL, parm, decl);
}
else
{
/* add const decl to namespace */
tree val;
if (requires_type)
{
error ("template use error: value provided where type needed");
continue;
}
#if 0
try to coerce value to proper type;
if (failed)
goto oops;
if (not constant)
goto oops;
pushdecl ();
#else
val = digest_init (TREE_TYPE (parm), arg, (tree *) 0);
if (val != error_mark_node)
{
decl = build_decl (VAR_DECL, DECL_NAME (parm), TREE_TYPE (parm));
DECL_INITIAL (decl) = val;
TREE_READONLY (decl) = 1;
}
#endif
}
if (decl != 0)
{
layout_decl (decl, 0);
if (class_level)
pushdecl_class_level (decl);
else
pushdecl (decl);
}
}
}
void
pop_template_decls (template, class_level)
tree template;
int class_level;
{
tree parmlist, arglist;
int i, nparms;
assert (template != 0);
assert (TREE_CODE (template) == TREE_LIST);
arglist = TREE_VALUE (template);
assert (TREE_CODE (arglist) == TREE_VEC);
template = TREE_PURPOSE (template);
assert (TREE_CODE (template) == TEMPLATE_DECL);
parmlist = DECL_ARGUMENTS (template);
assert (TREE_CODE (parmlist) == TREE_VEC);
nparms = TREE_VEC_LENGTH (parmlist);
assert (DECL_NAME (template) != 0);
for (i = 0; i < nparms; i++)
{
int requires_type, is_type;
tree parm = TREE_VEC_ELT (parmlist, i);
tree arg = TREE_VEC_ELT (arglist, i);
requires_type = TREE_CODE (parm) == IDENTIFIER_NODE;
is_type = TREE_CODE_CLASS (TREE_CODE (arg)) == 't';
if (is_type)
{
/* add typename to namespace */
tree decl;
if (!requires_type)
{
error ("template use error: type provided where value needed");
continue;
}
if (class_level)
{
tree t = IDENTIFIER_CLASS_VALUE (parm);
assert (t != 0);
assert (TREE_CODE_CLASS (TREE_CODE (t)) == 'd');
IDENTIFIER_CLASS_VALUE (parm) = NULL_TREE;
}
else
{
tree t = IDENTIFIER_LOCAL_VALUE (parm);
assert (t != 0);
assert (TREE_CODE_CLASS (TREE_CODE (t)) == 'd');
/* This isn't right, but this should get popped off. */
IDENTIFIER_LOCAL_VALUE (parm) = NULL_TREE;
}
adjust_type_value (parm);
}
}
}
/* Should be defined in cplus-tab.h. */
extern int yychar;
void
instantiate_template (class)
tree class;
{
struct template_info *template_info;
tree template, t1, t2;
assert (TREE_CODE (class) == IDENTIFIER_NODE);
template = IDENTIFIER_TEMPLATE (class);
if (TREE_TYPE (class))
return;
#if 0
fprintf (stderr, " [class %s] ", IDENTIFIER_POINTER (class));
#endif
push_to_top_level ();
pushlevel (0);
push_template_decls (template, 0);
template = TREE_PURPOSE (template);
assert (TREE_CODE (template) == TEMPLATE_DECL);
set_current_level_tags_transparency (1);
template_info = (struct template_info *) DECL_LANG_SPECIFIC (template);
feed_input (template_info->text, template_info->length, (struct obstack *)0);
input_filename = template_info->filename;
lineno = template_info->lineno;
yychar = PRE_PARSED_CLASS_DECL;
overload_template_name (class);
yylval.ttype = build_tree_list (class_type_node, class);
oops:
return;
}
void
undo_template_name_overload (id)
tree id;
{
tree template;
template = IDENTIFIER_TEMPLATE (id);
if (!template)
return;
assert (TREE_CODE (template) == TREE_LIST);
template = TREE_PURPOSE (template);
assert (template != NULL_TREE);
template = DECL_NAME (template);
assert (template != NULL_TREE);
poplevel (0, 0, 0);
IDENTIFIER_LOCAL_VALUE (template) = NULL_TREE;
SET_IDENTIFIER_TYPE_VALUE (template, NULL_TREE);
}
void
overload_template_name (id)
tree id;
{
tree template, t, decl;
assert (id != 0);
assert (TREE_CODE (id) == IDENTIFIER_NODE);
template = IDENTIFIER_TEMPLATE (id);
if (!template)
return;
template = TREE_PURPOSE (template);
assert (template != NULL_TREE);
template = DECL_NAME (template);
assert (template != NULL_TREE);
/* Push a level, so we don't overwrite the template decl. */
pushlevel (1);
t = xref_tag (class_type_node, id, NULL_TREE);
assert (TREE_CODE (t) == RECORD_TYPE);
decl = build_decl (TYPE_DECL, template, t);
pushdecl (decl);
IDENTIFIER_LOCAL_VALUE (template) = decl;
SET_IDENTIFIER_TYPE_VALUE (template, t);
}
/* T1 is PRE_PARSED_CLASS_DECL; T3 is result of XREF_TAG lookup. */
void
end_template_instantiation (t1, t3)
tree t1, t3;
{
extern struct pending_input *to_be_restored;
tree t, decl;
#if 0
fprintf (stderr, " [END class %s] ", IDENTIFIER_POINTER (TREE_VALUE (t1)));
#endif
/* Restore the old parser input state. */
if (yychar == -2 /* YYEMPTY */)
yychar = yylex ();
if (yychar != END_OF_SAVED_INPUT)
error ("parse error at end of class template");
else
{
restore_pending_input (to_be_restored);
to_be_restored = 0;
}
/* Our declarations didn't get stored in the global slot, since
there was a (supposedly tags-transparent) scope in between. */
t = IDENTIFIER_TYPE_VALUE (TREE_VALUE (t1));
assert (t != NULL_TREE && TREE_CODE_CLASS (TREE_CODE (t)) == 't');
decl = IDENTIFIER_GLOBAL_VALUE (TREE_VALUE (t1));
assert (decl != 0);
assert (TREE_CODE (decl) == TYPE_DECL);
undo_template_name_overload (TREE_VALUE (t1));
pop_template_decls (IDENTIFIER_TEMPLATE (TREE_VALUE (t1)), 0);
/* PRE_PARSED_CLASS_DECL pushes a level */
poplevel (0, 0, 0);
pop_from_top_level ();
/* This will fix up the type-value field. */
pushdecl_top_level (decl);
}
extern struct obstack permanent_obstack;
reinit_parse_for_template (yychar, d1, d2)
int yychar;
tree d1, d2;
{
current_template_info->filename = input_filename;
current_template_info->lineno = lineno;
reinit_parse_for_block (yychar, &permanent_obstack, 1);
current_template_info->text = obstack_base (&permanent_obstack);
current_template_info->length = obstack_object_size (&permanent_obstack);
obstack_finish (&permanent_obstack);
}
#if 0
/* Type unification.
We have a function template signature with one or more references to
template parameters, and a parameter list we wish to fit to this
template. If possible, produce a list of parameters for the template
which will cause it to fit the supplied parameter list.
Return zero for success, 2 for an incomplete match that doesn't resolve
all the types, and 1 for complete failure. An error message will be
printed only for an incomplete match.
TPARMS[NTPARMS] is an array of template parameter types;
TARGS[NTPARMS] is the array of template parameter values. PARMS is
the function template's signature (using TEMPLATE_PARM_IDX nodes),
and ARGS is the argument list we're trying to match against it. */
static int unify1 ();
int
type_unification (tparms, targs, ntparms, parms, args)
tree *tparms, *targs, parms, args;
{
tree parm, arg;
int i;
parm = parms.first;
arg = args.first;
while (parm && arg)
{
switch (unify (tparms, targs, ntparms, parm, arg)) {
case 0:
break;
case 1:
return 2;
}
next parm;
next arg;
}
if (parm || arg)
return 1;
for (i = 0; i < ntparms; i++)
if (!targs[i])
{
error ("incomplete type unification");
return 2;
}
return 0;
}
static int
unify1 (tparms, targs, ntparms, parm, arg)
tree *tparms, *targs, parm, arg;
{
if (parm == arg)
return 0;
switch (TREE_CODE (parm)) {
case TEMPLATE_PARM_IDX:
int idx = TREE_INT_CST_LOW (parm);
if (targs[idx] == arg)
return 0;
else if (targs[idx])
return 1;
else if (typeof arg != tparms[idx])
return 1;
targs[idx] = arg;
return 0;
default:
/* Do I do strict matching or promotions? */
abort ();
}
/* No simple match; recurse. */
abort ();
}
#endif