5.12.3.2.1.2.1.2. 解析函数体
对于在 14368 行所见的,跟在“ ) ”后面的关键字“ return ”,其解释参见章节 为方法构建节点 。
14356 static tree
14357 cp_parser_function_definition_after_declarator (cp_parser* parser, in parser.c
14358 bool inline_p)
14359 {
14360 tree fn;
14361 bool ctor_initializer_p = false;
14362 bool saved_in_unbraced_linkage_specification_p;
14363 unsigned saved_num_template_parameter_lists;
14364
14365 /* If the next token is `return', then the code may be trying to
14366 make use of the "named return value" extension that G++ used to
14367 support. */
14368 if (cp_lexer_next_token_is_keyword (parser->lexer, RID_RETURN))
14369 {
14370 /* Consume the `return' keyword. */
14371 cp_lexer_consume_token (parser->lexer);
14372 /* Look for the identifier that indicates what value is to be
14373 returned. */
14374 cp_parser_identifier (parser);
14375 /* Issue an error message. */
14376 error ("named return values are no longer supported");
14377 /* Skip tokens until we reach the start of the function body. */
14378 while (cp_lexer_next_token_is_not (parser->lexer, CPP_OPEN_BRACE)
14379 && cp_lexer_next_token_is_not (parser->lexer, CPP_EOF))
14380 cp_lexer_consume_token (parser->lexer);
14381 }
14382 /* The `extern' in `extern "C" void f () { ... }' does not apply to
14383 anything declared inside `f'. */
14384 saved_in_unbraced_linkage_specification_p
14385 = parser->in_unbraced_linkage_specification_p;
14386 parser->in_unbraced_linkage_specification_p = false;
14387 /* Inside the function, surrounding template-parameter-lists do not
14388 apply. */
14389 saved_num_template_parameter_lists
14390 = parser->num_template_parameter_lists;
14391 parser->num_template_parameter_lists = 0;
14392 /* If the next token is `try', then we are looking at a
14393 function-try-block. */
14394 if (cp_lexer_next_token_is_keyword (parser->lexer, RID_TRY))
14395 ctor_initializer_p = cp_parser_function_try_block (parser);
14396 /* A function-try-block includes the function-body, so we only do
14397 this next part if we're not processing a function-try-block. */
14398 else
14399 ctor_initializer_p
14400 = cp_parser_ctor_initializer_opt_and_function_body (parser);
14401
14402 /* Finish the function. */
14403 fn = finish_function ((ctor_initializer_p ? 1 : 0) |
14404 (inline_p ? 2 : 0));
14405 /* Generate code for it, if necessary. */
14406 expand_or_defer_fn (fn);
14407 /* Restore the saved values. */
14408 parser->in_unbraced_linkage_specification_p
14409 = saved_in_unbraced_linkage_specification_p;
14410 parser->num_template_parameter_lists
14411 = saved_num_template_parameter_lists;
14412
14413 return fn;
14414 }
5.12.3.2.1.2.1.2.1. 准备阶段
虽然我们用作例子的方法包含了空的函数体,不过处理空函数体仍然包括了相当复杂的过程。
11469 static bool
11470 cp_parser_ctor_initializer_opt_and_function_body (cp_parser *parser) in parser.c
11471 {
11472 tree body;
11473 bool ctor_initializer_p;
11474
11475 /* Begin the function body. */
11476 body = begin_function_body ();
11477 /* Parse the optional ctor-initializer. */
11478 ctor_initializer_p = cp_parser_ctor_initializer_opt (parser);
11479 /* Parse the function-body. */
11480 cp_parser_function_body (parser);
11481 /* Finish the function body. */
11482 finish_function_body (body);
11483
11484 return ctor_initializer_p;
11485 }
函数体本身是一个复合语句,它具有以下规则:
compound-statement:
{ statement-seq [opt] }
statement-seq 被一对大括号所包含,这对大括号构成了一个作用域来把这个 statement-seq 与外部分隔开来。因此在真正处理函数体前,它需要某些准备工作。
10756 tree
10757 begin_function_body (void) in decl.c
10758 {
10759 tree stmt;
10760
10761 if (processing_template_decl )
10762 /* Do nothing now. */;
10763 else
10764 /* Always keep the BLOCK node associated with the outermost pair of
10765 curly braces of a function. These are needed for correct
10766 operation of dwarfout.c. */
10767 keep_next_level (true);
10768
10769 stmt = begin_compound_stmt (/*has_no_scope=*/ false);
10770 COMPOUND_STMT_BODY_BLOCK (stmt) = 1;
10771
10772 if (processing_template_decl )
10773 /* Do nothing now. */;
10774 else if (DECL_CONSTRUCTOR_P (current_function_decl ))
10775 begin_constructor_body ();
10776 else if (DECL_DESTRUCTOR_P (current_function_decl ))
10777 begin_destructor_body ();
10778
10779 return stmt;
10780 }
看到对于这次调用参数 has_no_scope 是 false ,它表示该复合语句定义了一个作用域。
998 tree
999 begin_compound_stmt (bool has_no_scope) in semantics.c
1000 {
1001 tree r;
1002 int is_try = 0;
1003
1004 r = build_stmt (COMPOUND_STMT, NULL_TREE);
1005
1006 if (last_tree && TREE_CODE (last_tree) == TRY_BLOCK)
1007 is_try = 1;
1008
1009 add_stmt (r);
1010 if (has_no_scope)
1011 COMPOUND_STMT_NO_SCOPE (r) = 1;
1012
1013 last_expr_type = NULL_TREE;
1014
1015 if (!has_no_scope)
1016 do_pushlevel (is_try ? sk_try : sk_block);
1017 else
1018 /* Normally, we try hard to keep the BLOCK for a
1019 statement-expression. But, if it's a statement-expression with
1020 a scopeless block, there's nothing to keep, and we don't want
1021 to accidentally keep a block *inside* the scopeless block. */
1022 keep_next_level (false);
1023
1024 return r;
1025 }
在中间树里,大括号及中括号都不能出现。为了标记在该复合语句中的所有的语句,在语句链表的头部构建一个 COMPOUND_STMT 节点。在 84 行,在 begin_stmt_tree 中, last_expr_filename 被更新为 input_filename 。而如果当前输入文件不是当前函数所在的文件,构建 FILE_STMT 节点来记录这个不同。
81 tree
82 add_stmt (tree t) in c-semantics.c
83 {
84 if (input_filename != last_expr_filename)
85 {
86 /* If the filename has changed, also add in a FILE_STMT. Do a string
87 compare first, though, as it might be an equivalent string. */
88 int add = (strcmp (input_filename, last_expr_filename) != 0);
89 last_expr_filename = input_filename;
90 if (add)
91 {
92 tree pos = build_nt (FILE_STMT, get_identifier (input_filename));
93 add_stmt (pos);
94 }
95 }
96
97 /* Add T to the statement-tree. */
98 TREE_CHAIN (last_tree) = t;
99 last_tree = t;
100
101 /* When we expand a statement-tree, we must know whether or not the
102 statements are full-expressions. We record that fact here. */
103 STMT_IS_FULL_EXPR_P (last_tree) = stmts_are_full_exprs_p ();
104
105 return t;
106 }
注意到语句被加到语句链表的末尾,不过 FUNCTION_DECL 的 saved_tree 域仍旧指向该链表的末尾。如果当前语句是一个完整的表达式(即,在该语句中构建的临时对象,在语句的末尾被毁灭),函数 stmts_are_full_exprs_p 返回 true 。在 cxx_push_function_context 的 11299 行,设置了 stmts_are_full_exprs_p 域为 1 。
306 int
307 stmts_are_full_exprs_p (void) in semantics.c
308 {
309 return current_stmt_tree ()->stmts_are_full_exprs_p;
310 }
那么如果该复合语句定义了一个作用域,将加入一个新的 sk_block 或 try_block 作用域。
364 void
365 do_pushlevel (scope_kind sk) in semantics.c
366 {
367 if (stmts_are_full_exprs_p ())
368 {
369 if (!processing_template_decl )
370 add_scope_stmt (/*begin_p=*/ 1, /*partial_p=*/ 0);
371 begin_scope (sk, NULL);
372 }
373 }
如果不是一个模板声明,向语句链表插入一个 SCOPE_STMT 节点。参数 begin_p 表示该语句是否开启或结束一个作用域;对于一个部分作用域(即在一个标签( label )后开始的作用域,而该标签处构建了一个需要清除操作的对象), partial_p 是 true 。在 C++ 中,作用域可以被其他作用域完全包含,但不会彼此部分重叠。因此栈是适合记录作用域的,其中新的节点代表新的作用域。
131 tree
132 add_scope_stmt (int begin_p, int partial_p) in c-semantics.c
133 {
134 tree *stack_ptr = current_scope_stmt_stack ();
135 tree ss;
136 tree top = *stack_ptr;
137
138 /* Build the statement. */
139 ss = build_stmt (SCOPE_STMT, NULL_TREE);
140 SCOPE_BEGIN_P (ss) = begin_p;
141 SCOPE_PARTIAL_P (ss) = partial_p;
142
143 /* Keep the scope stack up to date. */
144 if (begin_p)
145 {
146 top = tree_cons (ss, NULL_TREE, top);
147 *stack_ptr = top;
148 }
149 else
150 {
151 if (partial_p != SCOPE_PARTIAL_P (TREE_PURPOSE (top)))
152 abort ();
153 TREE_VALUE (top) = ss;
154 *stack_ptr = TREE_CHAIN (top);
155 }
156
157 /* Add the new statement to the statement-tree. */
158 add_stmt (ss);
159
160 return top;
161 }
上面提及的 SCOPE_STMT 栈由 current_scope_stmt_stack 返回。
1199 tree *
1200 current_scope_stmt_stack (void) in c-semantics.c
1201 {
1202 return &cfun ->language->base.x_scope_stmt_stack;
1203 }
在 151 行, SCOPE_PARTIAL_P 表示部分作用域( partial scope ),例如:
S s;
l:
S s2;
goto l;
在‘ 1 ’后面是一个(隐含的)新的作用域,即便没有大括号。特别的,当我们执行 goto ,我们必须破坏 s2 然后再重新构建它。
那么在进行函数体解析前,以下的决定将被构建。这里我们忽略 last_tree 节点,因为它总是指向链表的末尾。
( 点此打开 )
图 94 :在解析函数体之前