您的位置:首页 > 编程语言

GCC-3.4.6源代码学习笔记(91)

2010-08-26 11:39 501 查看
5.12.3.1.2. 处理模板参数
现在我们从cp_parser_parameter_declaration返回,然后从cp_parser_template_parameter回到cp_parser_template_parameter_list。在这个函数中,变量parameter保存上图所示的树节点。

2161 tree
2162 process_template_parm (tree list, tree next) in pt.c
2163 {
2164 tree parm;
2165 tree decl = 0;
2166 tree defval;
2167 int is_type, idx;
2168
2169 parm = next;
2170 my_friendly_assert (TREE_CODE (parm) == TREE_LIST, 259);
2171 defval = TREE_PURPOSE (parm);
2172 parm = TREE_VALUE (parm);
2173 is_type = TREE_PURPOSE (parm) == class_type_node;
2174
2175 if (list)
2176 {
2177 tree p = TREE_VALUE (tree_last (list));
2178
2179 if (TREE_CODE (p) == TYPE_DECL || TREE_CODE (p) == TEMPLATE_DECL)
2180 idx = TEMPLATE_TYPE_IDX (TREE_TYPE (p));
2181 else
2182 idx = TEMPLATE_PARM_IDX (DECL_INITIAL (p));
2183 ++idx;
2184 }
2185 else
2186 idx = 0;

在上面的函数里,参数list是已经处理的模板参数列表。在这个链表中,为了快速找出参数,每个参数都被编号,并且该号码被保存在节点中。因此,首先需要找出上一次使用的号码,及第一个尚未使用的号码。
对于模板类型参数,tree_common部分的type域(由TREE_TYPE访问),及tree_decl的initial域(由DECL_INITIAL访问,不在普通的参数声明中使用)都是类型template_parm_index。其定义如下。

241 typedef struct template_parm_index_s GTY(()) in cp-tree.h
242 {
243 struct tree_common common;
244 HOST_WIDE_INT index;
245 HOST_WIDE_INT level;
246 HOST_WIDE_INT orig_level;
247 tree decl;
248 } template_parm_index;

为了访问相关的域,一组宏被定义。其中部分的定义如下。

3443 #define TEMPLATE_PARM_INDEX_CAST(NODE) / in cp-tree.h
3444 ((template_parm_index*)TEMPLATE_PARM_INDEX_CHECK (NODE))
3445 #define TEMPLATE_PARM_IDX(NODE) (TEMPLATE_PARM_INDEX_CAST (NODE)->index)
3446 #define TEMPLATE_PARM_LEVEL(NODE) (TEMPLATE_PARM_INDEX_CAST (NODE)->level)
3447 #define TEMPLATE_PARM_DESCENDANTS(NODE) (TREE_CHAIN (NODE))
3448 #define TEMPLATE_PARM_ORIG_LEVEL(NODE) (TEMPLATE_PARM_INDEX_CAST (NODE)->orig_level)
3449 #define TEMPLATE_PARM_DECL(NODE) (TEMPLATE_PARM_INDEX_CAST (NODE)->decl)

TEMPLATE_PARM_IDX给出参数的索引号(从0开始),而TEMPLATE_PARM_LEVEL给出参数的层级(从1开始)。这里有一个例子:

template <class T> // Index 0, Level 1, Orig Level 1.
struct S {
template <class U, // Index 0, Level 2, Orig Level 2.
class V> // Index 1, Level 2, Orig Level 2.
void f();
};

TEMPLATE_PARM_DESCENDANTS是从上面这个参数列表衍生出来的template_parm_index的链表。第一级衍生将具有相同的IDX,但其LEVEL将减1。所有的衍生参数都被TREE_CHAIN域链接在一起。而TEMPLATE_PARM_DECL是参数的声明,它是TYPE_DECL或者CONST_DECL。TEMPLATE_PARM_ORIG_LEVEL是距离最远的父亲的对应参数的层级,即,在声明参数时其最初的层级。例如,如果我们具现S<int>,我们将得到:

struct S<int> // N/A {
template <class U, // Index 0, Level 1, Orig Level 2
class V> // Index 1, Level 1, Orig Level 2
void f();
};

当我们考虑其中的类型时,参数的层级是LEVEL;而当我们考虑所具现的对象时,参数的层级ORIG_LEVEL。

process_template_parm (continue)

2188 if (!is_type)
2189 {

2212 }
2213 else
2214 {
2215 tree t;
2216 parm = TREE_VALUE (parm);
2217
2218 if (parm && TREE_CODE (parm) == TEMPLATE_DECL)
2219 {

2226 }
2227 else
2228 {
2229 t = make_aggr_type (TEMPLATE_TYPE_PARM);
2230 /* parm is either IDENTIFIER_NODE or NULL_TREE. */
2231 decl = build_decl (TYPE_DECL, parm, t);
2232 }
2233
2234 TYPE_NAME (t) = decl;
2235 TYPE_STUB_DECL (t) = decl;
2236 parm = decl;
2237 TEMPLATE_TYPE_PARM_INDEX (t)
2238 = build_template_parm_index (idx, processing_template_decl,
2239 processing_template_decl,
2240 decl, TREE_TYPE (parm));
2241 }
2242 DECL_ARTIFICIAL (decl) = 1;
2243 SET_DECL_TEMPLATE_PARM_P (decl);
2244 pushdecl (decl);
2245 parm = build_tree_list (defval, parm);
2246 return chainon (list, parm);
2247 }

我们的参数“Host”不是一个模板声明,一个简单的类类型将为其创建,它被认为是一个TYPE_DECL。

2104 static tree
2105 build_template_parm_index (int index, in pt.c
2106 int level,
2107 int orig_level,
2108 tree decl,
2109 tree type)
2110 {
2111 tree t = make_node (TEMPLATE_PARM_INDEX);
2112 TEMPLATE_PARM_IDX (t) = index;
2113 TEMPLATE_PARM_LEVEL (t) = level;
2114 TEMPLATE_PARM_ORIG_LEVEL (t) = orig_level;
2115 TEMPLATE_PARM_DECL (t) = decl;
2116 TREE_TYPE (t) = type;
2117 TREE_CONSTANT (t) = TREE_CONSTANT (decl);
2118 TREE_READONLY (t) = TREE_READONLY (decl);
2119
2120 return t;
2121 }

TEMPLATE_PARM_INDEX是具有类型template_parm_index的特殊树节点。它不会由用户产生,只有前端会构建它(DECL_ARTIFICIAL表示了这一事实)。
这个模板参数的作用域在sk_template_parms内。要向sk_template_parms加入该参数。

556 tree
557 pushdecl (tree x) in name-lookup.c
558 {
559 tree t;
560 tree name;
561 int need_new_binding;
562
563 timevar_push (TV_NAME_LOOKUP);
564
565 need_new_binding = 1;
566
567 if (DECL_TEMPLATE_PARM_P (x))
568 /* Template parameters have no context; they are not X::T even
569 when declared within a class or namespace. */
570 ;

604 name = DECL_NAME (x);
605 if (name)
606 {
607 int different_binding_level = 0;

615 /* In case this decl was explicitly namespace-qualified, look it
616 up in its namespace context. */
617 if (DECL_NAMESPACE_SCOPE_P (x) && namespace_bindings_p ())
618 t = namespace_binding (name, DECL_CONTEXT (x));
619 else
620 t = lookup_name_current_level (name);

这里的当前作用域是sk_template_parms,617行的条件不满足,因而尝试在当前作用域检查标识符是否已经被声明过。这里我们从lookup_name_current_level得到NULL。

4019 static tree
4020 lookup_name_current_level (tree name) in name-lookup.c
4021 {
4022 struct cp_binding_level *b;
4023 tree t = NULL_TREE;
4024
4025 timevar_push (TV_NAME_LOOKUP);
4026 b = innermost_nonclass_level ();
4027
4028 if (b->kind == sk_namespace)
4029 {
4030 t = IDENTIFIER_NAMESPACE_VALUE (name);
4031
4032 /* extern "C" function() */
4033 if (t != NULL_TREE && TREE_CODE (t) == TREE_LIST)
4034 t = TREE_VALUE (t);
4035 }
4036 else if (IDENTIFIER_BINDING (name)
4037 && LOCAL_BINDING_P (IDENTIFIER_BINDING (name)))
4038 {
4039 while (1)
4040 {
4041 if (IDENTIFIER_BINDING (name)->scope == b)
4042 POP_TIMEVAR_AND_RETURN (TV_NAME_LOOKUP, IDENTIFIER_VALUE (name));
4043
4044 if (b->kind == sk_cleanup)
4045 b = b->level_chain;
4046 else
4047 break;
4048 }
4049 }
4050
4051 POP_TIMEVAR_AND_RETURN (TV_NAME_LOOKUP, t);
4052 }

这个新的声明由下面800行的set_identifier_type_value安装到这个作用域中。在830行,namespace_binding_p返回false,因为最接近当前作用域的外部非类域是sk_template_parms。

pushdecl (continue)

772 /* If declaring a type as a typedef, copy the type (unless we're
773 at line 0), and install this TYPE_DECL as the new type's typedef
774 name. See the extensive comment in ../c-decl.c (pushdecl). */
775 if (TREE_CODE (x) == TYPE_DECL)
776 {
777 tree type = TREE_TYPE (x);

797 if (type != error_mark_node
798 && TYPE_NAME (type)
799 && TYPE_IDENTIFIER (type))
800 set_identifier_type_value (DECL_NAME (x), x);
801 }

828 /* This name is new in its binding level.
829 Install the new declaration and return it. */
830 if (namespace_bindings_p ())
831 {

872 }
873 else
874 {
875 /* Here to install a non-global value. */
876 tree oldlocal = IDENTIFIER_VALUE (name);
877 tree oldglobal = IDENTIFIER_NAMESPACE_VALUE (name);
878
879 if (need_new_binding)
880 {
881 push_local_binding (name, x, 0);
882 /* Because push_local_binding will hook X on to the
883 current_binding_level's name list, we don't want to
884 do that again below. */
885 need_new_binding = 0;
886 }
887
888 /* If this is a TYPE_DECL, push it into the type value slot. */
889 if (TREE_CODE (x) == TYPE_DECL)
890 set_identifier_type_value (name, x);

1003 }
1004
1005 if (TREE_CODE (x) == VAR_DECL)
1006 maybe_register_incomplete_var (x);
1007 }
1008
1009 if (need_new_binding)
1010 add_decl_to_level (x,
1011 DECL_NAMESPACE_SCOPE_P (x)
1012 ? NAMESPACE_LEVEL (CP_DECL_CONTEXT (x))
1013 : current_binding_level);
1014
1015 POP_TIMEVAR_AND_RETURN (TV_NAME_LOOKUP, x);
1016}

对于标识符节点,IDENTIFIER_VALUE域记录了定义了该名字的非名字空间绑定域,而IDNETIFIER_NAMESPACE_VALUE则记录了定义了该名字的名字空间作用域。因为“Host”是一个新的标识符,上面的oldlocal及oldglobal都是NULL。

1052 void
1053 push_local_binding (tree id, tree decl, int flags) in name-lookup.c
1054 {
1055 struct cp_binding_level *b;
1056
1057 /* Skip over any local classes. This makes sense if we call
1058 push_local_binding with a friend decl of a local class. */
1059 b = innermost_nonclass_level ();
1060
1061 if (lookup_name_current_level (id))
1062 {
1063 /* Supplement the existing binding. */
1064 if (!supplement_binding (IDENTIFIER_BINDING (id), decl))
1065 /* It didn't work. Something else must be bound at this
1066 level. Do not add DECL to the list of things to pop
1067 later. */
1068 return;
1069 }
1070 else
1071 /* Create a new binding. */
1072 push_binding (id, decl, b);
1073
1074 if (TREE_CODE (decl) == OVERLOAD || (flags & PUSH_USING))
1075 /* We must put the OVERLOAD into a TREE_LIST since the
1076 TREE_CHAIN of an OVERLOAD is already used. Similarly for
1077 decls that got here through a using-declaration. */
1078 decl = build_tree_list (NULL_TREE, decl);
1079
1080 /* And put DECL on the list of things declared by the current
1081 binding level. */
1082 add_decl_to_level (decl, b);
1083 }

显然,在加入前,需要再次检查该名字是否是新的。毫无疑问,对于“Host”,lookup_name_current_level返回NULL。最后,在1082行,add_decl_to_level把该名字记录到这个绑定域中。在返回pushdecl后,在890行,set_identifier_type_value把标识符的类型设置为这个TYPE_DECL.
在sk_template_parm域中装入模板参数“Host”后,我们会得到如下图的大致布局。



图49:加入“Host”后的大致布局

注意到current_template_parms记录了模板参数的嵌套层级。对于模板声明,其模板参数列表包含了形如template < template-parameter-list > class identifier [opt] 这样的参数, current_template_parms可以告知该参数所对应的级别。

2254 tree
2255 end_template_parm_list (tree parms) in pt.c
2256 {
2257 int nparms;
2258 tree parm, next;
2259 tree saved_parmlist = make_tree_vec (list_length (parms));
2260
2261 current_template_parms
2262 = tree_cons (size_int (processing_template_decl),
2263 saved_parmlist, current_template_parms);
2264
2265 for (parm = parms, nparms = 0; parm; parm = next, nparms++)
2266 {
2267 next = TREE_CHAIN (parm);
2268 TREE_VEC_ELT (saved_parmlist, nparms) = parm;
2269 TREE_CHAIN (parm) = NULL_TREE;
2270 }
2271
2272 --processing_template_parmlist;
2273
2274 return saved_parmlist;
2275 }

748 #define current_template_parms scope_chain->template_parms in cp-tree.h

在继续之前,考虑以下例子:
template <typename A> class BoxA {
public:
template<typename B, typename C> class BoxB {};
};

当处理类BoxB时,注意到它被包含在模板类BoxA中。显然,BoxB不仅依赖于类型参数B,而且亦依赖于(非显式)BoxA的类型参数。在处理BoxB的过程中把参数A记录为其目标参数的一部分,十分重要。



图50:嵌套模板参数的布局

在当前版本,当处理BoxB的声明时,processing_template_decl将是2(假定BoxA在全局名字空间中),那么在调用end_template_parm_list来完成BoxB的参数处理前, current_template_parms保存了purpose域为1的节点,这是BoxA的参数。执行完这个程序后,我们得到如上图的BoxB参数的布局。看到该链表保存了不同级别的参数,而每个节点的value域保存着同一级别的参数。在这个时刻,中间树看起来就像:



图51:处理了模板参数后的中间树
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: