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

5.12.4.2.2.2.2.          解析实参列表

然后调用 cp_parser_enclosed_template_argument_list 来解析 template-id 中所包含的模板实参列表。在这个处理之前, parser greater_than_is_operator_p 域将被设置为 false ,因为从现在起进来的“ > ”将不被视为操作符。而在实参列表中输入的“ >> ”将被解析作“ > ”和“ > ”,例如,“ A<B<>> a ”,前端将把它视为“ A<B<> > a ”。看到解析器将尝试从这个错误恢复并继续前行,但不保证可以产生正确的代码,因为程序员在这里犯了错误,因此给出错误信息。

 

14692 static tree

14693 cp_parser_enclosed_template_argument_list (cp_parser* parser)                 in parser.c

14694 {

14695    tree arguments;

14696    tree saved_scope;

14697    tree saved_qualifying_scope;

14698    tree saved_object_scope;

14699     bool saved_greater_than_is_operator_p;

14700

14701    /* [temp.names]

14702

14703      When parsing a template-id, the first non-nested `>' is taken as

14704      the end of the template-argument-list rather than a greater-than

14705      operator.  */

14706    saved_greater_than_is_operator_p

14707       = parser->greater_than_is_operator_p;

14708    parser->greater_than_is_operator_p = false;

14709    /* Parsing the argument list may modify SCOPE, so we save it

14710      here.  */

14711    saved_scope = parser->scope;

14712    saved_qualifying_scope = parser->qualifying_scope;

14713    saved_object_scope = parser->object_scope;

14714    /* Parse the template-argument-list itself.  */

14715    if (cp_lexer_next_token_is (parser->lexer, CPP_GREATER))

14716      arguments = NULL_TREE;

14717    else

14718      arguments = cp_parser_template_argument_list (parser);

14719    /* Look for the `>' that ends the template-argument-list. If we find

14720      a '>>' instead, it's probably just a typo.  */

14721    if (cp_lexer_next_token_is (parser->lexer, CPP_RSHIFT))

14722    {

14723      if (!saved_greater_than_is_operator_p)

14724      {

14725         /* If we're in a nested template argument list, the '>>' has to be

14726          a typo for '> >'. We emit the error message, but we continue

14727          parsing and we push a '>' as next token, so that the argument

14728          list will be parsed correctly..  */

14729        cp_token* token;

14730        error ("`>>' should be `> >' within a nested template argument list");

14731        token = cp_lexer_peek_token (parser->lexer);

14732        token->type = CPP_GREATER;

14733      }

14734      else

14735      {

14736        /* If this is not a nested template argument list, the '>>' is

14737          a typo for '>'. Emit an error message and continue.  */

14738        error ("spurious `>>', use `>' to terminate a template argument list");

14739        cp_lexer_consume_token (parser->lexer);

14740      }

14741    }

14742    else

14743      cp_parser_skip_until_found (parser, CPP_GREATER, "`>'");

14744    /* The `>' token might be a greater-than operator again now.  */

14745    parser->greater_than_is_operator_p

14746       = saved_greater_than_is_operator_p;

14747     /* Restore the SAVED_SCOPE.  */

14748    parser->scope = saved_scope;

14749    parser->qualifying_scope = saved_qualifying_scope;

14750    parser->object_scope = saved_object_scope;

14751

14752    return arguments;

14753 }

 

parser in_template_argument_list_p 域如果为 true 表示我们正在模板实参列表中。因此我们首先需要缓存这个域的内容,并在解析实参列表之前临时将其设为 true

 

8265 static tree

8266 cp_parser_template_argument_list (cp_parser* parser)                                  in parser.c

8267 {

8268    tree fixed_args[10];

8269    unsigned n_args = 0;

8270    unsigned alloced = 10;

8271    tree *arg_ary = fixed_args;

8272    tree vec;

8273    bool saved_in_template_argument_list_p;

8274

8275    saved_in_template_argument_list_p = parser->in_template_argument_list_p;

8276    parser->in_template_argument_list_p = true;

8277    do

8278    {

8279      tree argument;

8280

8281      if (n_args)

8282        /* Consume the comma.  */

8283        cp_lexer_consume_token (parser->lexer);

8284       

8285      /* Parse the template-argument.  */

8286      argument = cp_parser_template_argument (parser);

8287      if (n_args == alloced)

8288      {

8289        alloced *= 2;

8290  

8291        if (arg_ary == fixed_args)

8292        {

8293          arg_ary = xmalloc (sizeof (tree) * alloced);

8294          memcpy (arg_ary, fixed_args, sizeof (tree) * n_args);

8295        }

8296        else

8297          arg_ary = xrealloc (arg_ary, sizeof (tree) * alloced);

8298      }

8299      arg_ary[n_args++] = argument;

8300    }

8301    while (cp_lexer_next_token_is (parser->lexer, CPP_COMMA));

8302

8303    vec = make_tree_vec (n_args);

8304

8305    while (n_args--)

8306      TREE_VEC_ELT (vec, n_args) = arg_ary[n_args];

8307   

8308    if (arg_ary != fixed_args)

8309      free (arg_ary);

8310    parser->in_template_argument_list_p = saved_in_template_argument_list_p;

8311    return vec;

8312 }

 

在列表中的每个参数由 cp_parser_template_argument 解析。当所有的实参都被处理后,相应的所产生的节点被填充入 TREE_VEC 节点,并且该节点被作为返回值返回。

5.12.4.2.2.2.2.1.    解析实参

模板实参的语法树显示如下。

点此打开

根据【 8 】,在一个模板实参中,由 type-id id-expression 所导致的二义性,按 type-id 来解析,而不考虑对应模板实参的形式(缺省模板实参不存在这样的二义性,因为 template-parameter 的形式确定了缺省 template-argument 必须是 type-id )。

例如:

template <class T> void f();

template <int I> void f();

void g() {

f<int()>(); // int() is a type-id: call the first f()

}

因此我们首先尝试 type-id

 

8330 static tree

8331 cp_parser_template_argument (cp_parser* parser)                                       in parser.c

8332 {

8333    tree argument;

8334    bool template_p;

8335    bool address_p;

8336    bool maybe_type_id = false;

8337    cp_token *token;

8338    cp_id_kind idk;

8339    tree qualifying_class;

8340

8341    /* There's really no way to know what we're looking at, so we just

8342      try each alternative in order. 

8343

8344        [temp.arg]

8345

8346        In a template-argument, an ambiguity between a type-id and an

8347        expression is resolved to a type-id, regardless of the form of

8348        the corresponding template-parameter. 

8349

8350      Therefore, we try a type-id first.  */

8351    cp_parser_parse_tentatively (parser);

8352    argument = cp_parser_type_id (parser);

8353    /* If there was no error parsing the type-id but the next token is a '>>',

8354      we probably found a typo for '> >'. But there are type-id which are

8355      also valid expressions. For instance:

8356

8357          struct X { int operator >> (int); };

8358          template <int V> struct Foo {};

8359          Foo<X () >> 5> r;

8360

8361      Here 'X()' is a valid type-id of a function type, but the user just

8362      wanted to write the expression "X() >> 5". Thus, we remember that we

8363      found a valid type-id, but we still try to parse the argument as an

8364      expression to see what happens.  */

8365    if (!cp_parser_error_occurred (parser)

8366        && cp_lexer_next_token_is (parser->lexer, CPP_RSHIFT))

8367    {

8368      maybe_type_id = true;

8369      cp_parser_abort_tentative_parse (parser);

8370    }

8371    else

8372    {

8373      /* If the next token isn't a `,' or a `>', then this argument wasn't

8374        really finished. This means that the argument is not a valid

8375        type-id.  */

8376      if (!cp_parser_next_token_ends_template_argument_p (parser))

8377        cp_parser_error (parser, "expected template-argument");

8378      /* If that worked, we're done.  */

8379      if (cp_parser_parse_definitely (parser))

8380        return argument;

8381    }

 

这里有一个棘手的情形需要小心处理。考虑在上面注释中给出的例子(这个例子不能通过编译,但不是因为 >> ):

struct X { int operator >> (int); };

template <int V> struct Foo {};

Foo<X() >> 5> r;

X() ”是一个 type-id 的实例,但“ X() >> 5 ”则是移位表达式的实例(这是 assignment-expression 的一个变形,其中“ X() ”是一个 postfix-expression )。因为解析器要尽可能多地在一次中解析符号,它将把该字符串解析为移位表达式。然而由于失误,程序员也可能将“ > > ”写作“ >> ”,而把 type-id 从解析器的角度伸展为移位表达式。因此当成功解析一个后跟“ >> ”的 type-id 时,需要记住这个可能性,并且接着尝试按移位表达式来解析(此时,模板参数是非类型的)。细节在下面 assignment-expression 的情况一节。

5.12.4.2.2.2.2.1.1.            type-id 的情形

从其语法树,可以看到 type-id 包含了 type-specifier-seq 及可选的 abstract- declarator 。在经过解析之后,这些部分被包含在一个 TREE_LIST 节点,在其中, type-specifier-seq purpose 域,而 abstract-declarator value 域中。

 

10924 static tree

10925 cp_parser_type_id (cp_parser* parser)                                                      in parser.c

10926 {

10927    tree type_specifier_seq;

10928    tree abstract_declarator;

10929

10930    /* Parse the type-specifier-seq.  */

10931    type_specifier_seq

10932      = cp_parser_type_specifier_seq (parser);

10933    if (type_specifier_seq == error_mark_node)

10934      return error_mark_node;

10935

10936    /* There might or might not be an abstract declarator.  */

10937    cp_parser_parse_tentatively (parser);

10938    /* Look for the declarator.  */

10939    abstract_declarator

10940      = cp_parser_declarator (parser, CP_PARSER_DECLARATOR_ABSTRACT, NULL,

10941                         /*parenthesized_p=*/ NULL,

10942                         /*member_p=*/ false);

10943    /* Check to see if there really was a declarator.  */

10944    if (!cp_parser_parse_definitely (parser))

10945      abstract_declarator = NULL_TREE;

10946

10947    return groktypename (build_tree_list (type_specifier_seq,

10948                                   abstract_declarator));

10949 }

 

type-specifier-seq 的语法给出如下。

type-specifier-seq

type-specifier type-specifier-seq [opt]

GUN Ext attributes type-specifier-seq

 

10964 static tree

10965 cp_parser_type_specifier_seq (cp_parser* parser)                                      in parser.c

10966 {

10967    bool seen_type_specifier = false;

10968    tree type_specifier_seq = NULL_TREE;

10969

10970    /* Parse the type-specifiers and attributes.  */

10971    while (true)

10972    {

10973      tree type_specifier;

10974

10975      /* Check for attributes first.  */

10976      if (cp_lexer_next_token_is_keyword (parser->lexer, RID_ATTRIBUTE))

10977      {

10978        type_specifier_seq = tree_cons (cp_parser_attributes_opt (parser),

10979                                  NULL_TREE,

10980                                  type_specifier_seq);

10981        continue ;

10982      }

10983

10984       /* After the first type-specifier, others are optional.  */

10985      if (seen_type_specifier)

10986        cp_parser_parse_tentatively (parser);

10987       /* Look for the type-specifier.  */

10988      type_specifier = cp_parser_type_specifier (parser,

10989                                         CP_PARSER_FLAGS_NONE,

10990                                         /*is_friend=*/ false,

10991                                         /*is_declaration=*/ false,

10992                                         NULL,

10993                                         NULL);

10994      /* If the first type-specifier could not be found, this is not a

10995        type-specifier-seq at all.  */

10996      if (!seen_type_specifier && type_specifier == error_mark_node)

10997        return error_mark_node;

10998      /* If subsequent type-specifiers could not be found, the

10999        type-specifier-seq is complete.  */

11000      else if (seen_type_specifier && !cp_parser_parse_definitely (parser))

11001        break ;

11002

11003      /* Add the new type-specifier to the list.  */

11004      type_specifier_seq

11005         = tree_cons (NULL_TREE, type_specifier, type_specifier_seq);

11006      seen_type_specifier = true;

11007    }

11008

11009    /* We built up the list in reverse order.  */

11010    return nreverse (type_specifier_seq);

11011 }

 

函数 cp_parser_declarator 在这里被递归调用,不过仅是为了 abstract-declarator 。因为 type-id 在语义上是一个对象或函数的声明,其类型忽略对象或函数的名字,这个声明是一个类型的名字。根据解析的结果,应该由 groktypename 构建 *_TYPE 节点。

 

3640 tree

3641 groktypename (tree typename)                                                                   in decl.c

3642 {

3643    tree specs, attrs;

3644    tree type;

3645    if (TREE_CODE (typename) != TREE_LIST)

3646      return typename;

3647    split_specs_attrs (TREE_PURPOSE (typename), &specs, &attrs);

3648    type = grokdeclarator (TREE_VALUE (typename), specs,

3649                       TYPENAME, 0, &attrs);

3650    if (attrs)

3651      cplus_decl_attributes (&type, attrs, 0);

3652    return type;

3653 }

 

上面,属性及 type-specifier 被串接入同一个链表,需要把这个链表分成 2 个链表。下面的参数 declspecs 将持有 type-specifier-seq 部分,而 prefix_attributes 将保存属性链表如果出现的话。在 340 行的条件找出了一个整型常量,如‘ 5 ’等。而如果 spec_attrs 不是 tree_list ,则意味着没有属性。

 

334  void

335  split_specs_attrs (tree specs_attrs, tree *declspecs, tree *prefix_attributes)     in attribs.c

336  {

337    tree t, s, a, next, specs, attrs;

338 

339    /* This can happen after an __extension__ in pedantic mode.  */

340    if (specs_attrs != NULL_TREE

341        && TREE_CODE (specs_attrs) == INTEGER_CST)

342    {

343      *declspecs = NULL_TREE;

344      *prefix_attributes = NULL_TREE;

345      return ;

346    }

347 

348    /* This can happen in c++ (eg: decl: typespec initdecls ';').  */

349    if (specs_attrs != NULL_TREE

350        && TREE_CODE (specs_attrs) != TREE_LIST)

351    {

352      *declspecs = specs_attrs;

353      *prefix_attributes = NULL_TREE;

354      return ;

355    }

356 

357    /* Remember to keep the lists in the same order, element-wise.  */

358 

359    specs = s = NULL_TREE;

360    attrs = a = NULL_TREE;

361    for (t = specs_attrs; t; t = next)

362    {

363      next = TREE_CHAIN (t);

364      /* Declspecs have a non-NULL TREE_VALUE.  */

365      if (TREE_VALUE (t) != NULL_TREE)

366      {

367        if (specs == NULL_TREE)

368          specs = s = t;

369        else

370        {

371          TREE_CHAIN (s) = t;

372          s = t;

373        }

374      }

375      /* The TREE_PURPOSE may also be empty in the case of

376        __attribute__(()).  */

377      else if (TREE_PURPOSE (t) != NULL_TREE)

378      {

379        if (attrs == NULL_TREE)

380          attrs = a = TREE_PURPOSE (t);

381        else

382        {

383          TREE_CHAIN (a) = TREE_PURPOSE (t);

384          a = TREE_PURPOSE (t);

385        }

386         /* More attrs can be linked here, move A to the end.  */

387        while (TREE_CHAIN (a) != NULL_TREE)

388          a = TREE_CHAIN (a);

389      }

390    }

391 

392    /* Terminate the lists.  */

393    if (s != NULL_TREE)

394      TREE_CHAIN (s) = NULL_TREE;

395    if (a != NULL_TREE)

396      TREE_CHAIN (a) = NULL_TREE;

397 

398    /* All done.  */

399    *declspecs = specs;

400    *prefix_attributes = attrs;

401  }

 

对于 type-specifier-seq ,此处,可以看到 type-specifiers 总是通过 tree_list value 域串接起来,而 abstract-declarator 连同其属性总是由 purpose 域来串接。然后函数 grokdeclarator 为该对象或函数的类型返回相应的 TYPE_DECL 节点。在前面的章节中,我们已经看过这个函数的几个例子,因此这里我们跳过它。接下来 type-id 的属性将由 cplus_decl_attributes 安装入这个 TYPE_DECL 节点。

4.3.1.7.5.6.5. 处理内建函数的属性 一节中,我们已经看到前端使用数据结构 attribute_spec 来描述每个属性。表 common_attribute_table attribute_table format_attribute_table 用于 C++ ,而 format_attribute_table 用于 x86 (目标机器)。它们预定义了可以在语言中及指定平台上使用的所有属性。

 

1102 void

1103 cplus_decl_attributes (tree *decl, tree attributes, int flags)                                    in decl2.c

1104 {

1105    if (*decl == NULL_TREE || *decl == void_type_node)

1106      return ;

1107

1108    if (TREE_CODE (*decl) == TEMPLATE_DECL)

1109      decl = &DECL_TEMPLATE_RESULT (*decl);

1110

1111    decl_attributes (decl, attributes, flags);

1112

1113    if (TREE_CODE (*decl) == TYPE_DECL)

1114      SET_IDENTIFIER_TYPE_VALUE (DECL_NAME (*decl), TREE_TYPE (*decl));

1115 }

 

当调用 decl_attributes 时,我们可以通过向参数 flags 设置特定的 enum attribute_flags 的值,来指定要安装的属性,而没有安装的属性将通过一个链表返回。这里我们把 flags 指定为 0 ,表示安装所有提供的属性。

4.3.1.7.5.6.5. 处理内建函的属性 一节中,我们已经看到绝大多数属性具有关联的处理句柄,这些句柄将适当地修改树节点。除此之外,已应用的属性通过宏 *_ATTRIBUTES 链接入树节点的 attributes 域。这些宏同时也作为一个快速的方法来查看在该节点上应用了哪些属性。

5.12.4.2.2.2.2.1.2.            id-expression 的情形

如果这不是 type-id ,那么我们按 id-expression 重新解析符号。

 

cp_parser_template_argument (continue)

 

8382    /* We're still not sure what the argument will be.  */

8383    cp_parser_parse_tentatively (parser);

8384    /* Try a template.  */

8385    argument = cp_parser_id_expression (parser,

8386                                    /*template_keyword_p=*/ false,

8387                                     /*check_dependency_p=*/ true,

8388                                    &template_p,

8389                                    /*declarator_p=*/ false);

8390    /* If the next token isn't a `,' or a `>', then this argument wasn't

8391      really finished.  */

8392    if (!cp_parser_next_token_ends_template_argument_p (parser))

8393      cp_parser_error (parser, "expected template-argument");

8394    if (!cp_parser_error_occurred (parser))

8395    {

8396      /* Figure out what is being referred to. If the id-expression

8397         was for a class template specialization, then we will have a

8398         TYPE_DECL at this point. There is no need to do name lookup

8399         at this point in that case.  */

8400      if (TREE_CODE (argument) != TYPE_DECL)

8401        argument = cp_parser_lookup_name (parser, argument,

8402                                       /*is_type=*/ false,

8403                                       /*is_template=*/ template_p,

8404                                        /*is_namespace=*/ false,

8405                                       /*check_dependency=*/ true);

8406      if (TREE_CODE (argument) != TEMPLATE_DECL

8407          && TREE_CODE (argument) != UNBOUND_CLASS_TEMPLATE)

8408        cp_parser_error (parser, "expected template-name");

8409    }

8410    if (cp_parser_parse_definitely (parser))

8411      return argument;

 

5.12.3.2.1.1.3.4.1. 解析声明符 一节中已经给出了一个 cp_parser_id_expression 的例子。如果查找的结果不是 TYPE_DECL CPP_TEMPLATE_ID ,如果没有发生错误,它必须是一个标识符;那么这个名字将进一步被查找以找出关联的声明,就像 8401 行所作的那样。

5.12.4.2.2.2.2.1.3.            assignment-expression 的情形

如果我们按 type-id 解析成功,并且看到后面跟着“ >> ”,或者上面的尝试都失败了,该模板实参可能是 assignment-expression 的形式。在这个情形下,这个模板实参必须是非类型实参。根据 [3] ,非类型模板实参可以是:

一个具有整型或枚举类型的整形常量表达式;或者

一个非类型模板参数的名字;或者

表达为 & id-expression ,其中 & 是可选的,如果该名字引用一个函数或数组,具有外部链接性的对象或函数的地址,包括函数模板及函数 template-id ,但不包括的非静态类成员;或者对应的模板参数是一个引用,具有外部链接性的对象或函数;或者

指向一个成员的指针

 

cp_parser_template_argument (continue)

 

8412    /* It must be a non-type argument. There permitted cases are given

8413      in [temp.arg.nontype]:

8414

8415      -- an integral constant-expression of integral or enumeration

8416        type; or

8417

8418      -- the name of a non-type template-parameter; or

8419

8420      -- the name of an object or function with external linkage...

8421

8422      -- the address of an object or function with external linkage...

8423

8424      -- a pointer to member...  */

8425     /* Look for a non-type template parameter.  */

8426    if (cp_lexer_next_token_is (parser->lexer, CPP_NAME))

8427    {

8428      cp_parser_parse_tentatively (parser);

8429      argument = cp_parser_primary_expression (parser,

8430                                          &idk,

8431                                          &qualifying_class);

8432      if (TREE_CODE (argument) != TEMPLATE_PARM_INDEX

8433         || !cp_parser_next_token_ends_template_argument_p (parser))

8434        cp_parser_simulate_error (parser);

8435      if (cp_parser_parse_definitely (parser))

8436        return argument;

8437    }

 

在按 assignment-expression 解析该模板实参期间,如果我们看到第一个符号是一个标识符,从图形: 语句语法树 ,可以看到它一定是 assignment-expression 语法树中 primary-expression 部分的头部。如果 primary-expression 能被成功解析,我们就做完了。

否则,就检查在 primary-expression 之前是否有引导的 & 。这样形式的实参对应于后 2 种形式的非类型实参(记住该实参必须是编译时常量,对于指向成员的指针,它必须像:“ &A::f ”)。同样如果模板实参可以被解析为 primary-expression ,我们就完成了。我们不进入 primary-expression 的处理细节。在 5.12.4.1.2. 非类型参数 一节中可以找到一些相关的解释。

 

cp_parser_template_argument (continue)

 

8438    /* If the next token is "&", the argument must be the address of an

8439      object or function with external linkage.  */

8440    address_p = cp_lexer_next_token_is (parser->lexer, CPP_AND);

8441    if (address_p)

8442      cp_lexer_consume_token (parser->lexer);

8443    /* See if we might have an id-expression.  */

8444    token = cp_lexer_peek_token (parser->lexer);

8445    if (token->type == CPP_NAME

8446        || token->keyword == RID_OPERATOR

8447        || token->type == CPP_SCOPE

8448        || token->type == CPP_TEMPLATE_ID

8449        || token->type == CPP_NESTED_NAME_SPECIFIER)

8450    {

8451      cp_parser_parse_tentatively (parser);

8452      argument = cp_parser_primary_expression (parser,

8453                                          &idk,

8454                                          &qualifying_class);

8455      if (cp_parser_error_occurred (parser)

8456          || !cp_parser_next_token_ends_template_argument_p (parser))

8457        cp_parser_abort_tentative_parse (parser);

8458      else

8459      {

8460        if (qualifying_class)

8461          argument = finish_qualified_id_expr (qualifying_class,

8462                                          argument,

8463                                         /*done=*/ true,

8464                                         address_p);

8465        if (TREE_CODE (argument) == VAR_DECL)

8466        {

8467          /* A variable without external linkage might still be a

8468             valid constant-expression, so no error is issued here

8469             if the external-linkage check fails.  */

8470          if (!DECL_EXTERNAL_LINKAGE_P (argument))

8471            cp_parser_simulate_error (parser);

8472        }

8473        else if (is_overloaded_fn (argument))

8474          /* All overloaded functions are allowed; if the external

8475             linkage test does not pass, an error will be issued

8476             later.  */

8477          ;

8478        else if (address_p

8479              && (TREE_CODE (argument) == OFFSET_REF

8480                  || TREE_CODE (argument) == SCOPE_REF))

8481          /* A pointer-to-member.  */

8482          ;

8483        else

8484          cp_parser_simulate_error (parser);

8485

8486        if (cp_parser_parse_definitely (parser))

8487        {

8488          if (address_p)

8489            argument = build_x_unary_op (ADDR_EXPR, argument);

8490          return argument;

8491         }

8492      }

8493    }

8494     /* If the argument started with "&", there are no other valid

8495      alternatives at this point.  */

8496    if (address_p)

8497    {

8498      cp_parser_error (parser, "invalid non-type template argument");

8499      return error_mark_node;

8500    }

 

上面 qualifying_class cp_parser_primary_expression 设置,表示 pointer-to-member 中的限定类。如果设置了 qualifyng_class ,在 8461 行, finish_qualified_id_expr qualifying_class 的上下文查找 argument ,并为形如:“ A::f ”或“ a.*f ”的表达式构建相应的 OFFSET_REF 节点。然后在 8489 行, build_x_unary_op 为形如:“ &A::f ”的表达式构建相应的 ADDR_EXPR 节点。

如果上面的尝试都失败,那么看一下 assignment-expression 的语法树,根据【 3 】的要求,非类型模板实参必须是常量表达式。在这个语法树中,第二及第三规则不是常量表达式;而且常量表达式( constant-expression )可直接降为条件表达式( xxx? xxx: xxx )。

assignment-expression

condition-expression

|              condition-expression

logical-or-expression assignment-operator assignment-expression

throw-expression

因此下一步我们尝试常量表达式。看到下面的 maybe_type_id 如果是 true 表示一开始按 type-id 来解析是成功的,并且其后跟着“ >> ”。因此如果 maybe_type_id true ,意味着我们要尝试把模板实参按移位表达式来解析,它为常量表达式语法所涵盖。

 

cp_parser_template_argument (continue)

 

8501     /* If the argument wasn't successfully parsed as a type-id followed

8502      by '>>', the argument can only be a constant expression now. 

8503      Otherwise, we try parsing the constant-expression tentatively,

8504      because the argument could really be a type-id.  */

8505    if (maybe_type_id)

8506      cp_parser_parse_tentatively (parser);

8507    argument = cp_parser_constant_expression (parser,

8508                                        /*allow_non_constant_p=*/ false,

8509                                         /*non_constant_p=*/ NULL);

8510    argument = fold_non_dependent_expr (argument);

8511    if (!maybe_type_id)

8512      return argument;

8513    if (!cp_parser_next_token_ends_template_argument_p (parser))

8514      cp_parser_error (parser, "expected template-argument");

8515    if (cp_parser_parse_definitely (parser))

8516      return argument;

8517    /* We did our best to parse the argument as a non type-id, but that

8518      was the only alternative that matched (albeit with a '>' after

8519      it). We can assume it's just a typo from the user, and a

8520      diagnostic will then be issued.  */

8521    return cp_parser_type_id (parser);

8522 }

 

而如果 maybe_type_id false ,这是我们最后的机会,返回这个结果,不检查是否发生错误(错误消息由处理函数产生)。否则,需要确认该移位表达式正确构成了实参——即在它之后是否跟着“ , ”或“ > ”(注意,如果错把正确的非类型实参给了类型参数,这里是无法查出的,但在后面的处理中会发现实参与参数类型的不匹配)。如果不能,重新按 type-id 来解析符号,因为很可能是误把“ > > ”写作“ >> ”,这样编译器可以给出更合理的错误信息(看回 cp_parser_enclosed_template_argument_list ,它还将处理额外的“ >> ”返回)。

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值