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

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_declinitial域(由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_DECLTEMPLATE_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_parms617行的条件不满足,因而尝试在当前作用域检查标识符是否已经被声明过。这里我们从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”是一个新的标识符,上面的oldlocaloldglobal都是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

50:嵌套模板参数的布局

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

 

51

51:处理了模板参数后的中间树

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值