5.12.3.2.1.1.1. 解析类体 – typedef 声明
5.12.3.2.1.1.4.1. 找出 decl-specifier-spec 及 declarator
退出结构体“ Lock ”的定义,现在回到类“ SingleThreaded ”的定义。接下来的声明是“ typedef Host VolatileType; ”。
对于这个声明,“ typedef Host ”部分是 decl-specifier-seq ,而“ VolatileType ”就是声明符。再一次,我们的旅程从 cp_parser_member_declaration 开始,这个函数逐个处理类的方法。
12457 static void
12458 cp _parser_member_declaration (cp_parser* parser) in parser.c
12459 {
12460 tree decl_specifiers;
12461 tree prefix_attributes;
12462 tree decl;
12463 int declares_class_or_enum;
12464 bool friend_p;
12465 cp_token *token;
12466 int saved_pedantic;
…
12502 /* Parse the decl-specifier-seq. */
12503 decl_specifiers
12504 = cp_parser_decl_specifier_seq (parser,
12505 CP_PARSER_FLAGS_OPTIONAL,
12506 &prefix_attributes,
12507 &declares_class_or_enum);
关键字“ typedef ” 仅使得 cp_parser_decl_specifier_seq 返回表示“ typedef ”的全局唯一的标识符节点,这个节点在设置运行时环境时创建。同时标识符“ Host ”则经历以下的调用栈来找出对应的 TYPE_DECL 节点。
cp_parser_type_specifier à cp_parser_simple_type_specifier à cp_parser_type_name à cp_parser_class_name 。那么在 12504 行的 decl_specifiers 如下图所示。
( 点此打开 )
图 87 :对应 typedef 声明的 decl-specifier-seq
那么对于声明符“ VolatileType ”,通过以下的调用栈,它被识别为一个新的标识符: cp_parser_declarator à cp_parser_direct_declarator à cp_parser_declarator_id à cp_parser_id_expression à cp_parser_unqualified_id à cp_parser_identifier 。
cp_parser_member_declaration (continue)
12603 else
12604 {
12605 /* See if these declarations will be friends. */
12606 friend_p = cp_parser_friend_p (decl_specifiers);
12607
12608 /* Keep going until we hit the `;' at the end of the
12609 declaration. */
12610 while (cp_lexer_next_token_is_not (parser->lexer, CPP_SEMICOLON))
12611 {
12612 tree attributes = NULL_TREE;
12613 tree first_attribute;
12614
12615 /* Peek at the next token. */
12616 token = cp_lexer_peek_token (parser->lexer);
12617
12618 /* Check for a bitfield declaration. */
12619 if (token->type == CPP_COLON
12620 || (token->type == CPP_NAME
12621 && cp_lexer_peek_nth_token (parser->lexer, 2)->type
12622 == CPP_COLON))
12623 {
…
12657 }
12658 else
12659 {
12660 tree declarator;
12661 tree initializer;
12662 tree asm_specification;
12663 int ctor_dtor_or_conv_p;
12664
12665 /* Parse the declarator. */
12666 declarator
12667 = cp_parser_declarator (parser, CP_PARSER_DECLARATOR_NAMED,
12668 &ctor_dtor_or_conv_p,
12669 /*parenthesized_p=*/ NULL,
12670 /*member_p=*/ true);
…
12710 if (cp_lexer_next_token_is (parser->lexer, CPP_EQ))
12711 {
…
12730 }
12731 /* Otherwise, there is no initializer. */
12732 else
12733 initializer = NULL_TREE;
5.12.3.2.1.1.4.2. 为类字段构建节点
在解析了 decl-specifier-seq 及 declarator 后,需要把结果打包入相应的节点,并插入中间树中。
cp_parser_member_declaration (continue)
12735 /* See if we are probably looking at a function
12736 definition. We are certainly not looking at at a
12737 member-declarator. Calling `grokfield' has
12738 side-effects, so we must not do it unless we are sure
12739 that we are looking at a member-declarator. */
12740 if (cp_parser_token_starts_function_definition_p
12741 ( cp_lexer_peek_token (parser->lexer)))
12742 {
…
12763 }
12764 else
12765 {
12766 /* Create the declaration. */
12767 decl = grokfield (declarator, decl_specifiers,
12768 initializer, asm_specification,
12769 attributes);
12770 /* Any initialization must have been from a
12771 constant-expression. */
12772 if (decl && TREE_CODE (decl) == VAR_DECL && initializer)
12773 DECL_INITIALIZED_BY_CONSTANT_EXPRESSION_P (decl) = 1;
12774 }
12775 }
相应地如果这不是一个函数定义,它被假设为域来处理。在前一节中,我们已经看过特殊函数定义的处理——构造函数。
821 tree
822 grokfield (tree declarator, tree declspecs, tree init, tree asmspec_tree, in decl2.c
823 tree attrlist)
824 {
825 tree value;
826 const char *asmspec = 0;
827 int flags = LOOKUP_ONLYCONVERTING;
828
829 if (declspecs == NULL_TREE
830 && TREE_CODE (declarator) == SCOPE_REF
831 && TREE_CODE (TREE_OPERAND (declarator, 1)) == IDENTIFIER_NODE)
832 {
833 /* Access declaration */
834 if (! IS_AGGR_TYPE_CODE (TREE_CODE (TREE_OPERAND (declarator, 0))))
835 ;
836 else if (TREE_COMPLEXITY (declarator) == current_class_depth)
837 pop_nested_class ();
838 return do_class_using_decl (declarator);
839 }
840
841 if (init
842 && TREE_CODE (init) == TREE_LIST
843 && TREE_VALUE (init) == error_mark_node
844 && TREE_CHAIN (init) == NULL_TREE)
845 init = NULL_TREE;
846
847 value = grokdeclarator (declarator, declspecs, FIELD, init != 0, &attrlist);
848 if (! value || error_operand_p (value))
849 /* friend or constructor went bad. */
850 return error_mark_node;
该函数也需要 grokdeclarator 来为域声明创建合适的树节点。在这次调用中, initialized 是 false ,而 attrlist 是 NULL_TREE 。
6462 tree
6463 grokdeclarator (tree declarator, in decl.c
6464 tree declspecs,
6465 enum decl_context decl_context,
6466 int initialized,
6467 tree* attrlist)
6468 {
6469 RID_BIT_TYPE specbits;
6470 int nclasses = 0;
6471 tree spec;
6472 tree type = NULL_TREE;
6473 int longlong = 0;
6474 int type_quals;
6475 int virtualp, explicitp, friendp, inlinep, staticp;
6476 int explicit_int = 0;
6477 int explicit_char = 0;
6478 int defaulted_int = 0;
6479 int extern_langp = 0;
6480 tree dependant_name = NULL_TREE;
6481
6482 tree typedef_decl = NULL_TREE;
6483 const char *name;
6484 tree typedef_type = NULL_TREE;
6485 int funcdef_flag = 0;
6486 enum tree_code innermost_code = ERROR_MARK;
6487 int bitfield = 0;
6488 #if 0
6489 /* See the code below that used this. */
6490 tree decl_attr = NULL_TREE;
6491 #endif
6492
6493 /* Keep track of what sort of function is being processed
6494 so that we can warn about default return values, or explicit
6495 return values which do not match prescribed defaults. */
6496 special_function_kind sfk = sfk_none;
6497
6498 tree dname = NULL_TREE;
6499 tree ctype = current_class_type ;
6500 tree ctor_return_type = NULL_TREE;
6501 enum overload_flags flags = NO_SPECIAL;
6502 tree quals = NULL_TREE;
6503 tree raises = NULL_TREE;
6504 int template_count = 0;
6505 tree in_namespace = NULL_TREE;
6506 tree returned_attrs = NULL_TREE;
6507 tree scope = NULL_TREE;
6508 tree parms = NULL_TREE;
6509
6510 RIDBIT_RESET_ALL (specbits);
6511 if (decl_context == FUNCDEF)
6512 funcdef_flag = 1, decl_context = NORMAL;
6513 else if (decl_context == MEMFUNCDEF)
6514 funcdef_flag = -1, decl_context = FIELD;
6515 else if (decl_context == BITFIELD)
6516 bitfield = 1, decl_context = FIELD;
6517
6518 /* Look inside a declarator for the name being declared
6519 and get it as a string, for an error message. */
6520 {
6521 tree *next = &declarator;
6522 tree decl;
6523 name = NULL;
6524
6525 while (next && *next)
6526 {
6527 decl = *next;
6528 switch (TREE_CODE (decl))
6529 {
…
6629 case IDENTIFIER_NODE:
6630 if (TREE_CODE (decl) == IDENTIFIER_NODE)
6631 dname = decl;
6632
6633 next = 0;
6634
6635 if (C_IS_RESERVED_WORD (dname))
6636 {
6637 error ("declarator-id missing; using reserved word `%D'",
6638 dname);
6639 name = IDENTIFIER_POINTER (dname);
6640 }
6641 else if (!IDENTIFIER_TYPENAME_P (dname))
6642 name = IDENTIFIER_POINTER (dname);
6643 else
6644 {
6645 my_friendly_assert (flags == NO_SPECIAL, 154);
6646 flags = TYPENAME_FLAG;
6647 ctor_return_type = TREE_TYPE (dname);
6648 sfk = sfk_conversion;
6649 if (is_typename_at_global_scope (dname))
6650 name = IDENTIFIER_POINTER (dname);
6651 else
6652 name = "<invalid operator>";
6653 }
6654 break ;
…
6784 }
6785 }
6786 }
正如我们之前所见,首先,函数处理声明符来找出 decl-specifier-seq 所描述对象。这里,在 6631 行,标识符“ VolatileType ”被赋予 dname 。在 6642 行, IDENTIFIER_POINTER 返回该标识符中的代表名字的字符串(即字符串“ VolatileType ”)。
然后在对声明符处理结果的一些检查后,开始处理 decl-specifier-seq 部分。这里的 decl-specifier-seq 的第一部分是关键字“ typedef ”。我们已经在函数 init_reswords 中看到,它的信息被记录在全局数组 ridpointers 里,因此在下面的 6929 行, RIDBIT_SET 把看到 typedef 的事实记录入 specbits 。
grokdeclarator (continue)
6828 /* Look through the decl specs and record which ones appear.
6829 Some typespecs are defined as built-in typenames.
6830 Others, the ones that are modifiers of other types,
6831 are represented by bits in SPECBITS: set the bits for
6832 the modifiers that appear. Storage class keywords are also in SPECBITS.
6833
6834 If there is a typedef name or a type, store the type in TYPE.
6835 This includes builtin typedefs such as `int'.
6836
6837 Set EXPLICIT_INT if the type is `int' or `char' and did not
6838 come from a user typedef.
6839
6840 Set LONGLONG if `long' is mentioned twice.
6841
6842 For C++, constructors and destructors have their own fast treatment. */
6843
6844 for (spec = declspecs; spec; spec = TREE_CHAIN (spec))
6845 {
6846 int i;
6847 tree id;
6848
6849 /* Certain parse errors slip through. For example,
6850 `int class;' is not caught by the parser. Try
6851 weakly to recover here. */
6852 if (TREE_CODE (spec) != TREE_LIST)
6853 return 0;
6854
6855 id = TREE_VALUE (spec);
6856
6857 /* If the entire declaration is itself tagged as deprecated then
6858 suppress reports of deprecated items. */
6859 if (!adding_implicit_members && id && TREE_DEPRECATED (id))
6860 {
6861 if (deprecated_state != DEPRECATED_SUPPRESS)
6862 warn_deprecated_use (id);
6863 }
6864
6865 if (TREE_CODE (id) == IDENTIFIER_NODE)
6866 {
6867 if (id == ridpointers [(int) RID_INT]
6868 || id == ridpointers [(int) RID_CHAR]
6869 || id == ridpointers [(int) RID_BOOL]
6870 || id == ridpointers [(int) RID_WCHAR])
6871 {
6872 if (type)
6873 {
6874 if (id == ridpointers [(int) RID_BOOL])
6875 error ("`bool' is now a keyword");
6876 else
6877 error ("extraneous `%T' ignored", id);
6878 }
6879 else
6880 {
6881 if (id == ridpointers [(int) RID_INT])
6882 explicit_int = 1;
6883 else if (id == ridpointers [(int) RID_CHAR])
6884 explicit_char = 1;
6885 type = TREE_TYPE (IDENTIFIER_GLOBAL_VALUE (id));
6886 }
6887 goto found;
6888 }
6889 /* C++ aggregate types. */
6890 if (IDENTIFIER_HAS_TYPE_VALUE (id))
6891 {
6892 if (type)
6893 error ("multiple declarations `%T' and `%T'", type, id);
6894 else
6895 type = IDENTIFIER_TYPE_VALUE (id);
6896 goto found;
6897 }
6898
6899 for (i = (int) RID_FIRST_MODIFIER; i <= (int) RID_LAST_MODIFIER; i++)
6900 {
6901 if (ridpointers [i] == id)
6902 {
6903 if (i == (int) RID_LONG && RIDBIT_SETP (i, specbits))
6904 {
6905 if (pedantic && ! in_system_header && warn_long_long )
6906 pedwarn ("ISO C++ does not support `long long'");
6907 if (longlong)
6908 error ("`long long long' is too long for GCC");
6909 else
6910 longlong = 1;
6911 }
6912 else if (RIDBIT_SETP (i, specbits))
6913 pedwarn ("duplicate `%s'", IDENTIFIER_POINTER (id));
6914
6915 /* Diagnose "__thread extern" or "__thread static". */
6916 if (RIDBIT_SETP (RID_THREAD, specbits))
6917 {
6918 if (i == (int)RID_EXTERN)
6919 error ("`__thread' before `extern'");
6920 else if (i == (int)RID_STATIC)
6921 error ("`__thread' before `static'");
6922 }
6923
6924 if (i == (int)RID_EXTERN
6925 && TREE_PURPOSE (spec) == error_mark_node)
6926 /* This extern was part of a language linkage. */
6927 extern_langp = 1;
6928
6929 RIDBIT_SET (i, specbits);
6930 goto found;
6931 }
6932 }
6933 }
6934 else if (TREE_CODE (id) == TYPE_DECL)
6935 {
6936 if (type)
6937 error ("multiple declarations `%T' and `%T'", type,
6938 TREE_TYPE (id));
6939 else
6940 {
6941 type = TREE_TYPE (id);
6942 TREE_VALUE (spec) = type;
6943 typedef_decl = id;
6944 }
6945 goto found;
6946 }
6947 if (type)
6948 error ("two or more data types in declaration of `%s'", name);
6949 else if (TREE_CODE (id) == IDENTIFIER_NODE)
6950 {
6951 tree t = lookup_name (id, 1);
6952 if (!t || TREE_CODE (t) != TYPE_DECL)
6953 error ("`%s' fails to be a typedef or built in type",
6954 IDENTIFIER_POINTER (id));
6955 else
6956 {
6957 type = TREE_TYPE (t);
6958 typedef_decl = t;
6959 }
6960 }
6961 else if (id != error_mark_node)
6962 /* Can't change CLASS nodes into RECORD nodes here! */
6963 type = id;
6964
6965 found: ;
6966 }
而 decl-specifier-seq 的第二部分是代表“ Host ”的 TYPE_DECL ,它被 6934 到 6949 的代码所处理。注意到从 6949 到 6960 行的代码只是处理发现的未能解析的名字。此时, declspecs 看起来如下:
( 点此打开 )
图 88 :处理 decl-specifier-seq 后的 declspecs
下面的 nclasses 记录了已经见到的存储类别声明符的数目。除了“ static __thread ”及“ extern __thread ”,每个声明符只允许有一个存储类别声明符。
grokdeclarator (continue)
6973 typedef_type = type;
…
7019 ctype = NULL_TREE;
…
7206 if (RIDBIT_ANY_SET (specbits))
7207 {
7208 if (RIDBIT_SETP (RID_STATIC, specbits)) nclasses++;
7209 if (RIDBIT_SETP (RID_EXTERN, specbits) && !extern_langp) nclasses++;
7210 if (RIDBIT_SETP (RID_THREAD, specbits)) nclasses++;
7211 if (decl_context == PARM && nclasses > 0)
7212 error ("storage class specifiers invalid in parameter declarations");
7213 if (RIDBIT_SETP (RID_TYPEDEF, specbits))
7214 {
7215 if (decl_context == PARM)
7216 error ("typedef declaration invalid in parameter declaration");
7217 nclasses++;
7218 }
7219 if (RIDBIT_SETP (RID_AUTO, specbits)) nclasses++;
7220 if (RIDBIT_SETP (RID_REGISTER, specbits)) nclasses++;
7221 if (!nclasses && !friendp && extern_langp)
7222 nclasses++;
7223 }
…
7249 if (nclasses > 1)
7250 error ("multiple storage classes in declaration of `%s'", name);
…
7930 if (RIDBIT_SETP (RID_TYPEDEF, specbits) && decl_context != TYPENAME)
7931 {
7932 tree decl;
7933
7934 /* Note that the grammar rejects storage classes
7935 in typenames, fields or parameters. */
7936 if (current_lang_name == lang_name_java)
7937 TYPE_FOR_JAVA (type) = 1;
7938
7939 if (decl_context == FIELD)
7940 {
7941 if (constructor_name_p (declarator, current_class_type ))
7942 pedwarn ("ISO C++ forbids nested type `%D' with same name as enclosing class",
7943 declarator);
7944 decl = build_lang_decl (TYPE_DECL, declarator, type);
7945 }
7946 else
7947 {
7948 decl = build_decl (TYPE_DECL, declarator, type);
7949 if (in_namespace || ctype)
7950 error ("%Jtypedef name may not be a nested-name-specifier", decl);
7951 if (!current_function_decl )
7952 DECL_CONTEXT (decl) = FROB_CONTEXT (current_namespace);
7953 }
7954
7955 /* If the user declares "typedef struct {...} foo" then the
7956 struct will have an anonymous name. Fill that name in now.
7957 Nothing can refer to it, so nothing needs know about the name
7958 change. */
7959 if (type != error_mark_node
7960 && declarator
7961 && TYPE_NAME (type)
7962 && TREE_CODE (TYPE_NAME (type)) == TYPE_DECL
7963 && TYPE_ANONYMOUS_P (type)
7964 /* Don't do this if there are attributes. */
7965 && (!attrlist || !*attrlist)
7966 && cp_type_quals (type) == TYPE_UNQUALIFIED)
7967 {
7968 tree oldname = TYPE_NAME (type);
7969 tree t;
7970
7971 /* Replace the anonymous name with the real name everywhere. */
7972 lookup_tag_reverse (type, declarator);
7973 for (t = TYPE_MAIN_VARIANT (type); t; t = TYPE_NEXT_VARIANT (t))
7974 if (TYPE_NAME (t) == oldname)
7975 TYPE_NAME (t) = decl;
7976
7977 if (TYPE_LANG_SPECIFIC (type))
7978 TYPE_WAS_ANONYMOUS (type) = 1;
7979
7980 /* If this is a typedef within a template class, the nested
7981 type is a (non-primary) template. The name for the
7982 template needs updating as well. */
7983 if (TYPE_LANG_SPECIFIC (type) && CLASSTYPE_TEMPLATE_INFO (type))
7984 DECL_NAME (CLASSTYPE_TI_TEMPLATE (type))
7985 = TYPE_IDENTIFIER (type);
7986
7987 /* FIXME remangle member functions; member functions of a
7988 type with external linkage have external linkage. */
7989 }
7990
7991 if (quals)
7992 {
7993 if (ctype == NULL_TREE)
7994 {
7995 if (TREE_CODE (type) != METHOD_TYPE)
7996 error ("%Jinvalid type qualifier for non-member function type",
7997 decl);
7998 else
7999 ctype = TYPE_METHOD_BASETYPE (type);
8000 }
8001 if (ctype != NULL_TREE)
8002 grok_method_quals (ctype, decl, quals);
8003 }
8004
8005 if (RIDBIT_SETP (RID_SIGNED, specbits)
8006 || (typedef_decl && C_TYPEDEF_EXPLICITLY_SIGNED (typedef_decl)))
8007 C_TYPEDEF_EXPLICITLY_SIGNED (decl) = 1;
8008
8009 bad_specifiers (decl, "type", virtualp, quals != NULL_TREE,
8010 inlinep, friendp, raises != NULL_TREE);
8011
8012 return decl;
8013 }
对于 typedef 声明,在创建了 TYPE_DEF 节点之后,绝大部分工作已经完成,在 8009 行, bad_specifier 检查不应该出现在声明符里的声明符。
5540 static void
5541 bad_specifiers (tree object, in decl.c
5542 const char* type,
5543 int virtualp,
5544 int quals,
5545 int inlinep,
5546 int friendp,
5547 int raises)
5548 {
5549 if (virtualp)
5550 error ("`%D' declared as a `virtual' %s", object, type);
5551 if (inlinep)
5552 error ("`%D' declared as an `inline' %s", object, type);
5553 if (quals)
5554 error ("`const' and `volatile' function specifiers on `%D' invalid in %s declaration",
5555 object, type);
5556 if (friendp)
5557 cp_error_at ("`%D' declared as a friend", object);
5558 if (raises
5559 && (TREE_CODE (object) == TYPE_DECL
5560 || (!TYPE_PTRFN_P (TREE_TYPE (object))
5561 && !TYPE_REFFN_P (TREE_TYPE (object))
5562 && !TYPE_PTRMEMFUNC_P (TREE_TYPE (object)))))
5563 cp_error_at ("`%D' declared with an exception specification", object);
5564 }
5.12.3.2.1.1.4.3. 加入类字段的节点
下面的 value 是为 typedef 语句构建的 TYPE_DECL 节点。在 881 行的 push_template_decl 之前,它具有如下的结构。
( 点此打开 )
图 89 : typedef 声明的 TYPE_DECL
同样,因为 current_class_type 是“ SingleThreaded ”,这是一个仍旧在定义中的模板,毫无疑问, push_template_decl 被调用来为 typedef 语句构建及插入相应的 TEMPLATE_DECL 节点。
grokfield (continue)
852 if (TREE_CODE (value) == TYPE_DECL && init)
853 {
854 error ("typedef `%D' is initialized (use __typeof__ instead)", value);
855 init = NULL_TREE;
856 }
857
858 /* Pass friendly classes back. */
859 if (value == void_type_node)
860 return value;
861
862 /* Pass friend decls back. */
863 if ((TREE_CODE (value) == FUNCTION_DECL
864 || TREE_CODE (value) == TEMPLATE_DECL)
865 && DECL_CONTEXT (value) != current_class_type )
866 return value;
867
868 if (DECL_NAME (value) != NULL_TREE
869 && IDENTIFIER_POINTER (DECL_NAME (value))[0] == '_'
870 && ! strcmp (IDENTIFIER_POINTER (DECL_NAME (value)), "_vptr"))
871 error ("member `%D' conflicts with virtual function table field name",
872 value);
873
874 /* Stash away type declarations. */
875 if (TREE_CODE (value) == TYPE_DECL)
876 {
877 DECL_NONLOCAL (value) = 1;
878 DECL_CONTEXT (value) = current_class_type ;
879
880 if (processing_template_decl )
881 value = push_template_decl (value);
882
883 return value;
884 }
…
995 }
接下来在 push_template_decl_real 中,同样 primary 也是 false ,因为我们不是在 sk_template_parm 域中。整个过程与我们之前看到的相同。那么我们将得到以下的中间树,而在 881 行的 value 指向所创建的 TEMPLATE_DECL 节点的 result 域。
( 点此打开 )
图 90 :插入 typedef 声明的节点
那么当从 grokfield 返回时, cp_parser_member_declaration 知道这是类的项,因此检查后续的符号,并在 12806 行由 finish_member_declaration 把该项加入类中。
cp_parser_member_declaration (continue)
12777 /* Reset PREFIX_ATTRIBUTES. */
12778 while (attributes && TREE_CHAIN (attributes) != first_attribute)
12779 attributes = TREE_CHAIN (attributes);
12780 if (attributes)
12781 TREE_CHAIN (attributes) = NULL_TREE;
12782
12783 /* If there is any qualification still in effect, clear it
12784 now; we will be starting fresh with the next declarator. */
12785 parser->scope = NULL_TREE;
12786 parser->qualifying_scope = NULL_TREE;
12787 parser->object_scope = NULL_TREE;
12788 /* If it's a `,', then there are more declarators. */
12789 if (cp_lexer_next_token_is (parser->lexer, CPP_COMMA))
12790 cp_lexer_consume_token (parser->lexer);
12791 /* If the next token isn't a `;', then we have a parse error. */
12792 else if (cp_lexer_next_token_is_not (parser->lexer,
12793 CPP_SEMICOLON))
12794 {
12795 cp_parser_error (parser, "expected `;'");
12796 /* Skip tokens until we find a `;'. */
12797 cp_parser_skip_to_end_of_statement (parser);
12798
12799 break ;
12800 }
12801
12802 if (decl)
12803 {
12804 /* Add DECL to the list of members. */
12805 if (!friend_p)
12806 finish_member_declaration (decl);
12807
12808 if (TREE_CODE (decl) == FUNCTION_DECL)
12809 cp_parser_save_default_args (parser, decl);
12810 }
12811 }
12812 }
12813
12814 cp_parser_require (parser, CPP_SEMICOLON, "`;'");
12815 }
finish_member_declaration 的细节参考章节 作为成员加入类域 ,解析之后,中间树现在如下:
( 点此打开 )
图 91 : 完成 typedef 语句