Class-specifier具有规则:class-specifier: class-head { member-specification [opt] }
11855 static tree
11856 cp_parser_class_specifier (cp_parser* parser) in parser.c
11857 {
11858 cp_token *token;
11859 tree type;
11860 tree attributes;
11861 int has_trailing_semicolon;
11862 bool nested_name_specifier_p;
11863 unsigned saved_num_template_parameter_lists;
11864 bool pop_p = false;
11865 tree scope = NULL_TREE;
11866
11867 push_deferring_access_checks (dk_no_deferred);
11868
11869 /* Parse the class-head. */
11870 type = cp_parser_class_head (parser,
11871 &nested_name_specifier_p,
11872 &attributes);
11873 /* If the class-head was a semantic disaster, skip the entire body
11874 of the class. */
11875 if (!type)
11876 {
11877 cp_parser_skip_to_end_of_block_or_statement (parser);
11878 pop_deferring_access_checks ();
11879 return error_mark_node;
11880 }
上面11867行,为这个class-specifier准备延迟访问控制检查,因为其类型为dk_no_deferred,那么在将这个节点从栈中弹出时,将立刻执行检查。
5.12.3.2.1.1.1. 解析class-head
class-head通过base-clause项描述了类之间的继承层次,除此之外,nested-name-specifier项描述了该类经被定义的绑定域。
class-head
├ class-key identifier [opt] base-clause [opt]
├ class-key nested-name-specifier [opt] base-clause [opt]
├ class-key nested-name-specifier [opt] template-id base-clause [opt]
GNU Ext ├ class-key attributes identifer [opt] base-clause [opt]
GNU Ext ├ class-key attributes nested-name-specifier [opt] base-clause [opt]
GNU Ext Ⅼ class-key attributes nested-name-specifier [opt] template-id base-clause [opt]
├ class
├ struct
Ⅼ union
因此在其处理函数中,参数nested_name_specifier_p被设为true,如果使用了某个涉及nested-name-specifier的产生式,否则就设为false。这里对于我们的例子中的第二条语句,它包含了class-head“class SingleThreaded”。
12030 static tree
12031 cp_parser_class_head (cp_parser* parser, in parser.c
12032 bool* nested_name_specifier_p,
12033 tree *attributes_p)
12034 {
12035 cp_token *token;
12036 tree nested_name_specifier;
12037 enum tag_types class_key;
12038 tree id = NULL_TREE;
12039 tree type = NULL_TREE;
12040 tree attributes;
12041 bool template_id_p = false;
12042 bool qualified_p = false;
12043 bool invalid_nested_name_p = false;
12044 bool invalid_explicit_specialization_p = false;
12045 bool pop_p = false;
12046 unsigned num_templates;
12047
12048 /* Assume no nested-name-specifier will be present. */
12049 *nested_name_specifier_p = false;
12050 /* Assume no template parameter lists will be used in defining the
12051 type. */
12052 num_templates = 0;
12053
12054 /* Look for the class-key. */
12055 class_key = cp_parser_class_key (parser);
12056 if (class_key == none_type)
12057 return error_mark_node;
12058
12059 /* Parse the attributes. */
12060 attributes = cp_parser_attributes_opt (parser);
12061
12062 /* If the next token is `::', that is invalid -- but sometimes
12063 people do try to write:
12064
12065 struct ::S {};
12066
12067 Handle this gracefully by accepting the extra qualifier, and then
12068 issuing an error about it later if this really is a
12069 class-head. If it turns out just to be an elaborated type
12070 specifier, remain silent. */
12071 if (cp_parser_global_scope_opt (parser, /*current_scope_valid_p=*/false))
12072 qualified_p = true;
12073
12074 push_deferring_access_checks (dk_no_check);
12075
12076 /* Determine the name of the class. Begin by looking for an
12077 optional nested-name-specifier. */
12078 nested_name_specifier
12079 = cp_parser_nested_name_specifier_opt (parser,
12080 /*typename_keyword_p=*/false,
12081 /*check_dependency_p=*/false,
12082 /*type_p=*/false,
12083 /*is_declaration=*/false);
跟在class-key后面的项应该是标识符或者属性。看到nested-name-specifier也是以标识符开头,解析器首先尝试nested-name-specifier。Nested-name-specifier可能包含以下成员。
nested-name-specifier
Ⅼ class-or-namespace-name::nested-name-specifier [opt]
├ namespace-name --- identifier
Ⅼ class-name
├ identifier
Ⅼ template-id
“SingleTheaded”或者“SingleThreaded {”都不是有效的nested-name-specifiers,因为它要求“::”作为结尾的符号。在12079行,cp_parser_nested_name_specifier_opt将向nested_name_specifier返回NULL。
14127 static tree
14128 cp_parser_global_scope_opt (cp_parser* parser, bool current_scope_valid_p) in parser.c
14129 {
14130 cp_token *token;
14131
14132 /* Peek at the next token. */
14133 token = cp_lexer_peek_token (parser->lexer);
14134 /* If we're looking at a `::' token then we're starting from the
14135 global namespace, not our current location. */
14136 if (token->type == CPP_SCOPE)
14137 {
14138 /* Consume the `::' token. */
14139 cp_lexer_consume_token (parser->lexer);
14140 /* Set the SCOPE so that we know where to start the lookup. */
14141 parser->scope = global_namespace;
14142 parser->qualifying_scope = global_namespace;
14143 parser->object_scope = NULL_TREE;
14144
14145 return parser->scope;
14146 }
14147 else if (!current_scope_valid_p)
14148 {
14149 parser->scope = NULL_TREE;
14150 parser->qualifying_scope = NULL_TREE;
14151 parser->object_scope = NULL_TREE;
14152 }
14153
14154 return NULL_TREE;
14155 }
在parser中,项scope表示进行名字查找的域。如果它是NULL_TREE,那么我们要在源程序中当前打开的域里查找名字。如果它不是NULL,它要么是TYPE,要么是NAMESPACE_DECL,对应于我们要查找的域。而项object_scope和qualifying_scope给出了上一次查找所在的域。如果使用了形如“x->y”或“x.y”的表达式,object_scope分别给出“*x”或者“x”的类型。而qualifying_scope用在形如“X::Y”的表达式,它指向X。
如果上面函数的参数current_scope_valid_p是false,表示依赖当前打开的域进行名字解析。对于类声明,这是显然的。
cp_parser_class_head (continue)
12084 /* If there was a nested-name-specifier, then there *must* be an
12085 identifier. */
12086 if (nested_name_specifier)
12087 {
…
12143 }
12144 /* Otherwise, the identifier is optional. */
12145 else
12146 {
12147 /* We don't know whether what comes next is a template-id,
12148 an identifier, or nothing at all. */
12149 cp_parser_parse_tentatively (parser);
12150 /* Check for a template-id. */
12151 id = cp_parser_template_id (parser,
12152 /*template_keyword_p=*/false,
12153 /*check_dependency_p=*/true,
12154 /*is_declaration=*/true);
12155 /* If that didn't work, it could still be an identifier. */
12156 if (!cp_parser_parse_definitely (parser))
12157 {
12158 if (cp_lexer_next_token_is (parser->lexer, CPP_NAME))
12159 id = cp_parser_identifier (parser);
12160 else
12161 id = NULL_TREE;
12162 }
12163 else
12164 {
12165 template_id_p = true;
12166 ++num_templates;
12167 }
12168 }
12169
12170 pop_deferring_access_checks ();
12171
12172 if (id)
12173 cp_parser_check_for_invalid_template_id (parser, id);
12174
12175 /* If it's not a `:' or a `{' then we can't really be looking at a
12176 class-head, since a class-head only appears as part of a
12177 class-specifier. We have to detect this situation before calling
12178 xref_tag, since that has irreversible side-effects. */
12179 if (!cp_parser_next_token_starts_class_definition_p (parser))
12180 {
12181 cp_parser_error (parser, "expected `{' or `:'");
12182 return error_mark_node;
12183 }
12184
12185 /* At this point, we're going ahead with the class-specifier, even
12186 if some other problem occurs. */
12187 cp_parser_commit_to_tentative_parse (parser);
12188 /* Issue the error about the overly-qualified name now. */
12189 if (qualified_p)
12190 cp_parser_error (parser,
12191 "global qualification of class name is invalid");
12192 else if (invalid_nested_name_p)
12193 cp_parser_error (parser,
12194 "qualified name does not name a class");
12195 else if (nested_name_specifier)
12196 {
…
12232 }
12233 /* An explicit-specialization must be preceded by "template <>". If
12234 it is not, try to recover gracefully. */
12235 if (at_namespace_scope_p ()
12236 && parser->num_template_parameter_lists == 0
12237 && template_id_p)
12238 {
12239 error ("an explicit specialization must be preceded by 'template <>'");
12240 invalid_explicit_specialization_p = true;
12241 /* Take the same action that would have been taken by
12242 cp_parser_explicit_specialization. */
12243 ++parser->num_template_parameter_lists;
12244 begin_specialization ();
12245 }
12246 /* There must be no "return" statements between this point and the
12247 end of this function; set "type "to the correct return value and
12248 use "goto done;" to return. */
12249 /* Make sure that the right number of template parameters were
12250 present. */
12251 if (!cp_parser_check_template_parameters (parser, num_templates))
12252 {
12253 /* If something went wrong, there is no point in even trying to
12254 process the class-definition. */
12255 type = NULL_TREE;
12256 goto done;
12257 }
若base-clause之前是template-id,这是一个类模板的特化形式,例如:“template <> class A <int>: public B { … };”。对于普通的模板类,这应该是一个标识符。template-id是一个复杂的结构,我们以后来看它。
5.12.3.2.1.1.1.1. 合理性检查
在继续之前,需要检查是否有错误发生。首先,符号“<”不能跟在type-specifier之后,例程cp_parser_check_for_invalid_template_id检查这个错误的情况。
1883 static void
1884 cp_parser_check_for_invalid_template_id (cp_parser* parser, in parser.c
1885 tree type)
1886 {
1887 ptrdiff_t start;
1888 cp_token *token;
1889
1890 if (cp_lexer_next_token_is (parser->lexer, CPP_LESS))
1891 {
1892 if (TYPE_P (type))
1893 error ("`%T' is not a template", type);
1894 else if (TREE_CODE (type) == IDENTIFIER_NODE)
1895 error ("`%s' is not a template", IDENTIFIER_POINTER (type));
1896 else
1897 error ("invalid template-id");
1898 /* Remember the location of the invalid "<". */
1899 if (cp_parser_parsing_tentatively (parser)
1900 && !cp_parser_committed_to_tentative_parse (parser))
1901 {
1902 token = cp_lexer_peek_token (parser->lexer);
1903 token = cp_lexer_prev_token (parser->lexer, token);
1904 start = cp_lexer_token_difference (parser->lexer,
1905 parser->lexer->first_token,
1906 token);
1907 }
1908 else
1909 start = -1;
1910 /* Consume the "<". */
1911 cp_lexer_consume_token (parser->lexer);
1912 /* Parse the template arguments. */
1913 cp_parser_enclosed_template_argument_list (parser);
1914 /* Permanently remove the invalid template arguments so that
1915 this error message is not issued again. */
1916 if (start >= 0)
1917 {
1918 token = cp_lexer_advance_token (parser->lexer,
1919 parser->lexer->first_token,
1920 start);
1921 cp_lexer_purge_tokens_after (parser->lexer, token);
1922 }
1923 }
1924 }
然后还需要查看接下来的符号是否可以构成有效的class-head,它必须是“{”或“:”。
15199 static bool
15200 cp_parser_next_token_starts_class_definition_p (cp_parser *parser) in parser.c
15201 {
15202 cp_token *token;
15203
15204 token = cp_lexer_peek_token (parser->lexer);
15205 return (token->type == CPP_OPEN_BRACE || token->type == CPP_COLON);
15206 }
接着对于我们的例子,变量template_id_p一直是false,因为声明不是一个template-id。并注意到现在变量num_templates仍旧是0。
14018 static bool
14019 cp_parser_check_template_parameters (cp_parser* parser, in parser.c
14020 unsigned num_templates)
14021 {
14022 /* If there are more template classes than parameter lists, we have
14023 something like:
14024
14025 template <class T> void S<T>::R<T>::f (); */
14026 if (parser->num_template_parameter_lists < num_templates)
14027 {
14028 error ("too few template-parameter-lists");
14029 return false;
14030 }
14031 /* If there are the same number of template classes and parameter
14032 lists, that's OK. */
14033 if (parser->num_template_parameter_lists == num_templates)
14034 return true;
14035 /* If there are more, but only one more, then we are referring to a
14036 member template. That's OK too. */
14037 if (parser->num_template_parameter_lists == num_templates + 1)
14038 return true;
14039 /* Otherwise, there are too many template parameter lists. We have
14040 something like:
14041
14042 template <class T> template <class U> void S::f(); */
14043 error ("too many template-parameter-lists");
14044 return false;
14045 }
正如我们在前面章节看到的,parser的域num_template_parameter_lists记录了模板参数的嵌套级别,而num_templates记录了,在cp_parser_class_head中,已经看到的模板嵌套级数。对于一个有效的声明,它们要相互匹配。
5.12.3.2.1.1.1.2. 加入类SingleThreaded的标签
现在在合理性检查后,对于被解析的符号,这里有3种情况:1)template-id,它可能构成了一个偏特化模板;2)没有发现nested-name-specified,它应该是一个类类型定义;3)发现nested-name-specifier,它可能是嵌套的类类型定义,或者类成员定义。我们需要一个一个来处理。
这里“标签”的作用,即是告诉编译器,此乃用户定义之类型。从此之后,这个类型即可用于type-specifier语法结构中(如果是前向声明,则是有限的使用,只能用于指针)。
cp_parser_class_head (continue)
12259 /* Look up the type. */
12260 if (template_id_p)
12261 {
12262 type = TREE_TYPE (id);
12263 maybe_process_partial_specialization (type);
12264 }
12265 else if (!nested_name_specifier)
12266 {
12267 /* If the class was unnamed, create a dummy name. */
12268 if (!id)
12269 id = make_anon_name ();
12270 type = xref_tag (class_key, id, /*globalize=*/false,
12271 parser->num_template_parameter_lists);
12272 }
12273 else
12274 {
…
12316 }
这里对于我们的例子,我们已经看到,变量nested_name_specifier是NULL,例程xref_tag被调用,其参数tag_code表示与name关联的标签(tag)的类型。而tag_type具有如下定义。
2954 enum tag_types { in cp-tree.h
2955 none_type = 0, /* Not a tag type. */
2956 record_type, /* "struct" types. */
2957 class_type, /* "class" types. */
2958 union_type, /* "union" types. */
2959 enum_type, /* "enum" types. */
2960 typename_type /* "typename" types. */
2961 };
另外,当这个名字代表一个定义时,参数globalize是false,仅在当前的作用域中查找该名字;当这个声明由一组模板参数前导,template_header_p是true(这里它是1,来自parser->num_template_parameter_lists)。
9443 tree
9444 xref_tag (enum tag_types tag_code, tree name, in decl.c
9445 bool globalize, bool template_header_p)
9446 {
9447 enum tree_code code;
9448 tree t;
9449 struct cp_binding_level *b = current_binding_level;
9450 tree context = NULL_TREE;
9451
9452 timevar_push (TV_NAME_LOOKUP);
9453
9454 my_friendly_assert (TREE_CODE (name) == IDENTIFIER_NODE, 0);
9455
9456 switch (tag_code)
9457 {
9458 case record_type:
9459 case class_type:
9460 code = RECORD_TYPE;
9461 break;
9462 case union_type:
9463 code = UNION_TYPE;
9464 break;
9465 case enum_type:
9466 code = ENUMERAL_TYPE;
9467 break;
9468 default:
9469 abort ();
9470 }
9471
9472 if (! globalize)
9473 {
9474 /* If we know we are defining this tag, only look it up in
9475 this scope and don't try to find it as a type. */
9476 t = lookup_tag (code, name, b, 1);
9477 }
9478 else
9479 {
...
9556 }
因为globalize是false,在调用lookup_tag的点,向其参数thislevel_only传入1,这个值表示仅查找指定的上下文(但跳过任意sk_cleanup的上下文,以找出对于标签有意义的上下文)。
2369 tree
2370 lookup_tag (enum tree_code form, tree name, in name-lookup.c
2371 cxx_scope *binding_level, int thislevel_only)
2372 {
2373 struct cp_binding_level *level;
2374 /* Nonzero if, we should look past a template parameter level, even
2375 if THISLEVEL_ONLY. */
2376 int allow_template_parms_p = 1;
2377 bool type_is_anonymous = ANON_AGGRNAME_P (name);
2378
2379 timevar_push (TV_NAME_LOOKUP);
2380 for (level = binding_level; level; level = level->level_chain)
2381 {
2382 tree tail;
2383 if (type_is_anonymous && level->type_decls != NULL)
2384 {
2385 tree type = binding_table_find_anon_type (level->type_decls, name);
2386 /* There is no need for error checking here, because
2387 anon names are unique throughout the compilation. */
2388 if (type != NULL)
2389 POP_TIMEVAR_AND_RETURN (TV_NAME_LOOKUP, type);
2390 }
2391 else if (level->kind == sk_namespace)
2392 /* Do namespace lookup. */
2393 for (tail = current_namespace; 1; tail = CP_DECL_CONTEXT (tail))
2394 {
2395 cxx_binding *binding =
2396 cxx_scope_find_binding_for_name (NAMESPACE_LEVEL (tail), name);
2397 tree old;
2398
2399 /* If we just skipped past a template parameter level,
2400 even though THISLEVEL_ONLY, and we find a template
2401 class declaration, then we use the _TYPE node for the
2402 template. See the example below. */
2403 if (thislevel_only && !allow_template_parms_p
2404 && binding && binding->value
2405 && DECL_CLASS_TEMPLATE_P (binding->value))
2406 old = binding->value;
2407 else if (binding)
2408 old = select_decl (binding, LOOKUP_PREFER_TYPES);
2409 else
2410 old = NULL_TREE;
2411
2412 if (old)
2413 {
...
2429 }
2430 if (thislevel_only || tail == global_namespace)
2431 POP_TIMEVAR_AND_RETURN (TV_NAME_LOOKUP, NULL_TREE);
2432 }
2433 else if (level->type_decls != NULL)
2434 {
...
2449 }
2450 if (thislevel_only && level->kind != sk_cleanup)
2451 {
2452 if (level->kind == sk_template_parms && allow_template_parms_p)
2453 {
2454 /* We must deal with cases like this:
2455
2456 template <class T> struct S;
2457 template <class T> struct S {};
2458
2459 When looking up `S', for the second declaration, we
2460 would like to find the first declaration. But, we
2461 are in the pseudo-global level created for the
2462 template parameters, rather than the (surrounding)
2463 namespace level. Thus, we keep going one more level,
2464 even though THISLEVEL_ONLY is nonzero. */
2465 allow_template_parms_p = 0;
2466 continue;
2467 }
2468 else
2469 POP_TIMEVAR_AND_RETURN (TV_NAME_LOOKUP, NULL_TREE);
2470 }
2471 }
2472 POP_TIMEVAR_AND_RETURN (TV_NAME_LOOKUP, NULL_TREE);
2473 }
首先看到如果thislevel_only是0,将从当前作用域直到全局名字空间,查找这个标签;而如果它是非0值,在指定的域中完成查找后,函数即退出。现在我们正在域“sk_template_parms”中进行声明(看到该域为begin_template_parm_list所加入),因此即便thislevel_only是1,我们也将重新进入这个循环。那么查找在名字空间中“Loki”中进行。当然cxx_scope_find_binding_for_name不应该在其中找到这个名字。作为结果lookup_tag返回NULL_TREE。
xref_tag (continue)
9558 if (! t)
9559 {
9560 /* If no such tag is yet defined, create a forward-reference node
9561 and record it as the "definition".
9562 When a real declaration of this type is found,
9563 the forward-reference will be altered into a real type. */
9564 if (code == ENUMERAL_TYPE)
9565 {
9566 error ("use of enum `%#D' without previous declaration", name);
9567 POP_TIMEVAR_AND_RETURN (TV_NAME_LOOKUP, error_mark_node);
9568 }
9569 else
9570 {
9571 t = make_aggr_type (code);
9572 TYPE_CONTEXT (t) = context;
9573 pushtag (name, t, globalize);
9574 }
9575 }
9576 else
9577 {
9578 if (!globalize && processing_template_decl && IS_AGGR_TYPE (t))
9579 redeclare_class_template (t, current_template_parms);
9580 else if (!processing_template_decl
9581 && CLASS_TYPE_P (t)
9582 && CLASSTYPE_IS_TEMPLATE (t))
9583 {
9584 error ("redeclaration of `%T' as a non-template", t);
9585 t = error_mark_node;
9586 }
9587 }
9588
9589 POP_TIMEVAR_AND_RETURN (TV_NAME_LOOKUP, t);
9590 }
当回到xref_tag时,在9558行,变量t将是NULL。RECORD_TYPE类型的树节点为该类型所创建。并看到到该名字将在9573行被pushtag加入到名字空间“Loki”中。
现在current_binding_level指向cxx_scope对象sk_template_parms。因此在下面4596行的WHILE循环中,b上升到名字空间“Loki”的作用域。
4589 void
4590 pushtag (tree name, tree type, int globalize) in name-lookup.c
4591 {
4592 struct cp_binding_level *b;
4593
4594 timevar_push (TV_NAME_LOOKUP);
4595 b = current_binding_level;
4596 while (/* Cleanup scopes are not scopes from the point of view of
4597 the language. */
4598 b->kind == sk_cleanup
4599 /* Neither are the scopes used to hold template parameters
4600 for an explicit specialization. For an ordinary template
4601 declaration, these scopes are not scopes from the point of
4602 view of the language -- but we need a place to stash
4603 things that will go in the containing namespace when the
4604 template is instantiated. */
4605 || (b->kind == sk_template_parms && b->explicit_spec_p)
4606 || (b->kind == sk_class
4607 && (globalize
4608 /* We may be defining a new type in the initializer
4609 of a static member variable. We allow this when
4610 not pedantic, and it is particularly useful for
4611 type punning via an anonymous union. */
4612 || COMPLETE_TYPE_P (b->this_entity))))
4613 b = b->level_chain;
4614
4615 if (b->type_decls == NULL)
4616 b->type_decls = binding_table_new (SCOPE_DEFAULT_HT_SIZE);
4617 binding_table_insert (b->type_decls, name, type);
这里name指向标识符“SingleThreaded”的节点,该名字之前没有声明,因此在下面的4622行,作为结果IDENTIFIER_TYPE_VALUE返回NULL。同样看到在xref_tag中,对于该节点,其TYPE_CONTEXT是NULL。
pushtag (continue)
4619 if (name)
4620 {
4621 /* Do C++ gratuitous typedefing. */
4622 if (IDENTIFIER_TYPE_VALUE (name) != type)
4623 {
4624 tree d = NULL_TREE;
4625 int in_class = 0;
4626 tree context = TYPE_CONTEXT (type);
4627
4628 if (! context)
4629 {
4630 tree cs = current_scope ();
4631
4632 if (! globalize)
4633 context = cs;
4634 else if (cs != NULL_TREE && TYPE_P (cs))
4635 /* When declaring a friend class of a local class, we want
4636 to inject the newly named class into the scope
4637 containing the local class, not the namespace scope. */
4638 context = decl_function_context (get_type_decl (cs));
4639 }
4640 if (!context)
4641 context = current_namespace;
4642
4643 if (b->kind == sk_class
4644 || (b->kind == sk_template_parms
4645 && b->level_chain->kind == sk_class))
4646 in_class = 1;
4647
4648 if (current_lang_name == lang_name_java)
4649 TYPE_FOR_JAVA (type) = 1;
4650
4651 d = create_implicit_typedef (name, type);
4652 DECL_CONTEXT (d) = FROB_CONTEXT (context);
4653 if (! in_class)
4654 set_identifier_type_value_with_scope (name, d, b);
例程current_scope将返回非空值,仅当当前的封装该类型的是类,或者函数。这里它返回NULL。那么在4641行,变量context被设置为名字空间“Loki”。
图52:加入类SingleThreaded的标签 – 第一步
在4654行向名字空间“Loki”加入这个名字后,中间树的布局大致如上图。