5.12.5.2.2.2.1.4. 完成具现
现在继续我们模板具现例子的处理,在 前端部分的初始化 一节,我们已经看到当使用命令行选项 -frepo 时, init_repo 会产生一个 .rpo 文件,或读入一个已经存在的 .rpo 文件。下面的 repo_template_used 将在对应的 .rpo 文件中记录该模板具现的使用,这个信息将被编译器及 collect2 ( GCC 自带的链接器)在以后使用。
instantiate_class_template (continue)
5473 /* Clear this now so repo_template_used is happy. */
5474 TYPE_BEING_DEFINED (type) = 0;
5475 repo_template_used (type);
5476
5477 /* Now that the class is complete, instantiate default arguments for
5478 any member functions. We don't do this earlier because the
5479 default arguments may reference members of the class. */
5480 if (!PRIMARY_TEMPLATE_P (template))
5481 for (t = TYPE_METHODS (type); t; t = TREE_CHAIN (t))
5482 if (TREE_CODE (t) == FUNCTION_DECL
5483 /* Implicitly generated member functions will not have template
5484 information; they are not instantiations, but instead are
5485 created "fresh" for each instantiation. */
5486 && DECL_TEMPLATE_INFO (t))
5487 tsubst_default_arguments (t);
5488
5489 popclass ();
5490 pop_from_top_level ();
5491 pop_deferring_access_checks ();
5492 pop_tinst_level ();
5493
5494 if (TYPE_CONTAINS_VPTR_P (type))
5495 keyed_classes = tree_cons (NULL_TREE, type, keyed_classes);
5496
5497 return type;
5498 }
然后记得通过 popclass 退出类的作用域;接着通过 pop_from_top_level 恢复原有的作用域。还有递减 tinst_depth 并通过 pop_tinst_level 更新 current_tinst_level ,它是 EXPR_WITH_FILE_LOCATION 类型的节点。因为类 C 包含 vtable ,它被视为锁定类( keyed class )。
从 instantiate_class_template 退出,我们回到 complete_type ,然后 layout_var_decl 。这个模板是在函数 main 中具现,因此 current_function_decl 指向该函数的作用域,那么在 layout_var_decl 的 4100 行, push_local_name 把这个变量加入函数的作用域。
948 static void
949 push_local_name (tree decl) in decl.c
950 {
951 size_t i, nelts;
952 tree t, name;
953
954 timevar_push (TV_NAME_LOOKUP);
955 if (!local_names)
956 VARRAY_TREE_INIT (local_names, 8, "local_names");
957
958 name = DECL_NAME (decl);
959
960 nelts = VARRAY_ACTIVE_SIZE (local_names);
961 for (i = 0; i < nelts; i++)
962 {
963 t = VARRAY_TREE (local_names, i);
964 if (DECL_NAME (t) == name)
965 {
966 if (!DECL_LANG_SPECIFIC (decl))
967 retrofit_lang_decl (decl);
968 DECL_LANG_SPECIFIC (decl)->decl_flags.u2sel = 1;
969 if (DECL_LANG_SPECIFIC (t))
970 DECL_DISCRIMINATOR (decl) = DECL_DISCRIMINATOR (t) + 1;
971 else
972 DECL_DISCRIMINATOR (decl) = 1;
973
974 VARRAY_TREE (local_names, i) = decl;
975 timevar_pop (TV_NAME_LOOKUP);
976 return ;
977 }
978 }
979
980 VARRAY_PUSH_TREE (local_names, decl);
981 timevar_pop (TV_NAME_LOOKUP);
982 }
上面的 local_names 是一个访问 cfun 的 language 部分 x_local_names 域的宏,这个域是一个可变的数组,保存了在由 cfun 所表示的函数中声明的布局名字。
5.12.5.2.2.2.1.5. 完成 VAR_DECL
现在我们回到了 cp_finish_decl ,曙光就在前面!
cp_finish_decl (continue)
4962 /* Output the assembler code and/or RTL code for variables and functions,
4963 unless the type is an undefined structure or union.
4964 If not, it will get done when the type is completed. */
4965 if (TREE_CODE (decl) == VAR_DECL || TREE_CODE (decl) == FUNCTION_DECL)
4966 {
4967 if (TREE_CODE (decl) == VAR_DECL)
4968 maybe_commonize_var (decl);
4969
4970 make_rtl_for_nonlocal_decl (decl, init, asmspec);
4971
4972 if (TREE_CODE (type) == FUNCTION_TYPE
4973 || TREE_CODE (type) == METHOD_TYPE)
4974 abstract_virtuals_error (decl,
4975 strip_array_types (TREE_TYPE (type)));
4976 else if (POINTER_TYPE_P (type) || TREE_CODE (type) == ARRAY_TYPE)
4977 {
…
4986 }
4987 else
4988 abstract_virtuals_error (decl, type);
4989
4990 if (TREE_CODE (decl) == FUNCTION_DECL
4991 || TREE_TYPE (decl) == error_mark_node)
4992 /* No initialization required. */
4993 ;
4994 else if (DECL_EXTERNAL (decl)
4995 && ! (DECL_LANG_SPECIFIC (decl)
4996 && DECL_NOT_REALLY_EXTERN (decl)))
4997 {
4998 if (init)
4999 DECL_INITIAL (decl) = init;
5000 }
5001 else
5002 {
5003 /* A variable definition. */
5004 if (DECL_FUNCTION_SCOPE_P (decl))
5005 {
5006 /* This is a local declaration. */
5007 maybe_inject_for_scope_var (decl);
5008 /* Initialize the local variable. */
5009 if (processing_template_decl)
5010 {
5011 if (init || DECL_INITIAL (decl) == error_mark_node)
5012 DECL_INITIAL (decl) = init;
5013 }
5014 else if (!TREE_STATIC (decl))
5015 initialize_local_var (decl, init);
5016 }
5017
5018 /* If a variable is defined, and then a subsequent
5019 definintion with external linkage is encountered, we will
5020 get here twice for the same variable. We want to avoid
5021 calling expand_static_init more than once. For variables
5022 that are not static data members, we can call
5023 expand_static_init only when we actually process the
5024 initializer. It is not legal to redeclare a static data
5025 member, so this issue does not arise in that case. */
5026 if (var_definition_p && TREE_STATIC (decl))
5027 expand_static_init (decl, init);
5028 }
5029 finish_end0:
5030
5031 /* Undo call to `pushclass' that was done in `start_decl'
5032 due to initialization of qualified member variable.
5033 i.e., Foo::x = 10; */
5034 {
5035 tree context = CP_DECL_CONTEXT (decl);
5036 if (context
5037 && TYPE_P (context)
5038 && (TREE_CODE (decl) == VAR_DECL
5039 /* We also have a pushclass done that we need to undo here
5040 if we're at top level and declare a method. */
5041 || TREE_CODE (decl) == FUNCTION_DECL)
5042 /* If size hasn't been set, we're still defining it,
5043 and therefore inside the class body; don't pop
5044 the binding level.. */
5045 && COMPLETE_TYPE_P (context)
5046 && context == current_class_type )
5047 pop_nested_class ();
5048 }
5049 }
5050
5051 /* If a CLEANUP_STMT was created to destroy a temporary bound to a
5052 reference, insert it in the statement-tree now. */
5053 if (cleanup)
5054 add_stmt (cleanup);
5055
5056 finish_end:
5057
5058 if (was_readonly)
5059 TREE_READONLY (decl) = 1;
5060
5061 /* If this was marked 'used', be sure it will be output. */
5062 if (lookup_attribute ("used", DECL_ATTRIBUTES (decl)))
5063 mark_referenced (DECL_ASSEMBLER_NAME (decl));
5064 }
在函数剩余的代码里,为了把模板的具现 obj_ 按照局部变量来处理,在 5015 行调用了 initialize_local_var 。
698 static void
4699 initialize_local_var (tree decl, tree init) in decl.c
4700 {
4701 tree type = TREE_TYPE (decl);
4702 tree cleanup;
4703
4704 my_friendly_assert (TREE_CODE (decl) == VAR_DECL
4705 || TREE_CODE (decl) == RESULT_DECL,
4706 20021010);
4707 my_friendly_assert (!TREE_STATIC (decl), 20021010);
4708
4709 if (DECL_SIZE (decl) == NULL_TREE)
4710 {
4711 /* If we used it already as memory, it must stay in memory. */
4712 DECL_INITIAL (decl) = NULL_TREE;
4713 TREE_ADDRESSABLE (decl) = TREE_USED (decl);
4714 }
4715
4716 if (DECL_SIZE (decl) && type != error_mark_node)
4717 {
4718 int already_used;
4719
4720 /* Compute and store the initial value. */
4721 already_used = TREE_USED (decl) || TREE_USED (type);
4722
4723 /* Perform the initialization. */
4724 if (init)
4725 {
4726 int saved_stmts_are_full_exprs_p;
4727
4728 my_friendly_assert (building_stmt_tree (), 20000906);
4729 saved_stmts_are_full_exprs_p = stmts_are_full_exprs_p ();
4730 current_stmt_tree ()->stmts_are_full_exprs_p = 1;
4731 finish_expr_stmt (init);
4732 current_stmt_tree ()->stmts_are_full_exprs_p =
4733 saved_stmts_are_full_exprs_p;
4734 }
4735
4736 /* Set this to 0 so we can tell whether an aggregate which was
4737 initialized was ever used. Don't do this if it has a
4738 destructor, so we don't complain about the 'resource
4739 allocation is initialization' idiom. Now set
4740 attribute((unused)) on types so decls of that type will be
4741 marked used. (see TREE_USED, above.) */
4742 if (TYPE_NEEDS_CONSTRUCTING (type)
4743 && ! already_used
4744 && TYPE_HAS_TRIVIAL_DESTRUCTOR (type)
4745 && DECL_NAME (decl))
4746 TREE_USED (decl) = 0;
4747 else if (already_used)
4748 TREE_USED (decl) = 1;
4749 }
4750
4751 /* Generate a cleanup, if necessary. */
4752 cleanup = cxx_maybe_build_cleanup (decl);
4753 if (DECL_SIZE (decl) && cleanup)
4754 finish_decl_cleanup (decl, cleanup);
4755 }
这里参数 init 来自声明符中跟在“ = ”后面的部分,它是由程序员指定的初始值,而不是在前面我们看到的由编译器指定的。显然,对于我们的例子, init 是 NULL 。
接着看 4752 行的 cxx_maybe_build_cleanup 。编译器可能会插入代码来清除局部变量,在它退出其作用域时。
11215 tree
11216 cxx_maybe_build_cleanup (tree decl) in decl.c
11217 {
11218 tree type = TREE_TYPE (decl);
11219
11220 if (type != error_mark_node && TYPE_HAS_NONTRIVIAL_DESTRUCTOR (type))
11221 {
11222 int flags = LOOKUP_NORMAL|LOOKUP_DESTRUCTOR;
11223 tree rval;
11224
11225 if (TREE_CODE (type) == ARRAY_TYPE)
11226 rval = decl;
11227 else
11228 {
11229 cxx_mark_addressable (decl);
11230 rval = build_unary_op (ADDR_EXPR, decl, 0);
11231 }
11232
11233 /* Optimize for space over speed here. */
11234 if (! TYPE_USES_VIRTUAL_BASECLASSES (type)
11235 || flag_expensive_optimizations )
11236 flags |= LOOKUP_NONVIRTUAL;
11237
11238 rval = build_delete (TREE_TYPE (rval), rval,
11239 sfk_complete_destructor, flags, 0);
11240
11241 if (TYPE_USES_VIRTUAL_BASECLASSES (type)
11242 && ! TYPE_HAS_DESTRUCTOR (type))
11243 rval = build_compound_expr (rval, build_vbase_delete (type, decl));
11244
11245 return rval;
11246 }
11247 return NULL_TREE;
11248 }
断言 TYPE_HAS_NONTRIVIAL_DESTRUCTOR 是 true ,如果该类型的析构函数不是平凡的(对于某个类,如果我们没有定义析构函数,并且对于所有的基类及非静态数据成员, TYPE_HAS_NONTRIVIAL_DESTRUCTOR 是 false ,那么对于这个类, TYPE_HAS_NONTRIVIAL_DESTRUCTOR 返回 false )。
这就是为什么如果析构函数不做任何事就不要定义它。虽然编译器会为你添加一个隐含的析构函数,但只要它的基类及非静态数据成员都具有平凡析构函数,它就会被标记为平凡。结果,在这里不会产生用于清除的代码。
如果期望清除操作,首先从局部变量 decl 构建出一个 ADDR_EXPR ,因为 delete 操作符总是作用于指针。在程序员使用 register 强制把变量放入寄存器的情形下, cxx_mark_addressable 将修改所产生的代码以表示把这个局部变量放入栈中。
2866 tree
2867 build_delete (tree type, tree addr, special_function_kind auto_delete, in init.c
2868 int flags, int use_global_delete)
2869 {
2870 tree expr;
2871
2872 if (addr == error_mark_node)
2873 return error_mark_node;
2874
2875 /* Can happen when CURRENT_EXCEPTION_OBJECT gets its type
2876 set to `error_mark_node' before it gets properly cleaned up. */
2877 if (type == error_mark_node)
2878 return error_mark_node;
2879
2880 type = TYPE_MAIN_VARIANT (type);
2881
2882 if (TREE_CODE (type) == POINTER_TYPE)
2883 {
…
2918 }
2919 else if (TREE_CODE (type) == ARRAY_TYPE)
2920 {
…
2930 }
2931 else
2932 {
2933 /* Don't check PROTECT here; leave that decision to the
2934 destructor. If the destructor is accessible, call it,
2935 else report error. */
2936 addr = build_unary_op (ADDR_EXPR, addr, 0);
2937 if (TREE_SIDE_EFFECTS (addr))
2938 addr = save_expr (addr);
2939
2940 addr = convert_force (build_pointer_type (type), addr, 0);
2941 }
2942
2943 my_friendly_assert (IS_AGGR_TYPE (type), 220);
2944
2945 if (TYPE_HAS_TRIVIAL_DESTRUCTOR (type))
2946 {
2947 if (auto_delete != sfk_deleting_destructor)
2948 return void_zero_node;
2949
2950 return build_op_delete_call
2951 (DELETE_EXPR, addr, cxx_sizeof_nowarn (type),
2952 LOOKUP_NORMAL | (use_global_delete * LOOKUP_GLOBAL),
2953 NULL_TREE);
2954 }
2955 else
2956 {
2957 tree do_delete = NULL_TREE;
2958 tree ifexp;
2959
2960 my_friendly_assert (TYPE_HAS_DESTRUCTOR (type), 20011213);
2961
2962 /* For `::delete x', we must not use the deleting destructor
2963 since then we would not be sure to get the global `operator
2964 delete'. */
2965 if (use_global_delete && auto_delete == sfk_deleting_destructor)
2966 {
2967 /* We will use ADDR multiple times so we must save it. */
2968 addr = save_expr (addr);
2969 /* Delete the object. */
2970 do_delete = build_builtin_delete_call (addr);
2971 /* Otherwise, treat this like a complete object destructor
2972 call. */
2973 auto_delete = sfk_complete_destructor;
2974 }
2975 /* If the destructor is non-virtual, there is no deleting
2976 variant. Instead, we must explicitly call the appropriate
2977 `operator delete' here. */
2978 else if (!DECL_VIRTUAL_P (CLASSTYPE_DESTRUCTORS (type))
2979 && auto_delete == sfk_deleting_destructor)
2980 {
2981 /* We will use ADDR multiple times so we must save it. */
2982 addr = save_expr (addr);
2983 /* Build the call. */
2984 do_delete = build_op_delete_call (DELETE_EXPR,
2985 addr,
2986 cxx_sizeof_nowarn (type),
2987 LOOKUP_NORMAL,
2988 NULL_TREE);
2989 /* Call the complete object destructor. */
2990 auto_delete = sfk_complete_destructor;
2991 }
2992 else if (auto_delete == sfk_deleting_destructor
2993 && TYPE_GETS_REG_DELETE (type))
2994 {
2995 /* Make sure we have access to the member op delete, even though
2996 we'll actually be calling it from the destructor. */
2997 build_op_delete_call (DELETE_EXPR, addr, cxx_sizeof_nowarn (type),
2998 LOOKUP_NORMAL, NULL_TREE);
2999 }
3000
3001 expr = build_dtor_call (build_indirect_ref (addr, NULL),
3002 auto_delete, flags);
3003 if (do_delete)
3004 expr = build (COMPOUND_EXPR, void_type_node, expr, do_delete);
3005
3006 if (flags & LOOKUP_DESTRUCTOR)
3007 /* Explicit destructor call; don't check for null pointer. */
3008 ifexp = integer_one_node;
3009 else
3010 /* Handle deleting a null pointer. */
3011 ifexp = fold (cp_build_binary_op (NE_EXPR, addr, integer_zero_node));
3012
3013 if (ifexp != integer_one_node)
3014 expr = build (COND_EXPR, void_type_node,
3015 ifexp, expr, void_zero_node);
3016
3017 return expr;
3018 }
3019 }
指示符 sfk_complete_destructor 表示析构对象但不回收内存,而 sfk_deleting_destructor 则要求回收对象的内存。如果 type 是一个类, delete 操作符可以在类里被重载( 2993 行的断言 TYPE_GETS_REG_DELETE 会识别出这个情形)。
在 2945 行是对平凡析构函数一个特殊处理。注意到这个函数不仅用于清除越出其作用域的局部变量,还被 delete 表达式用于释放堆上分配的对象。如果没有内存需要被回收,它不做任何事(而是返回 void_zero_node ),否则仅是为 delete 表达式构建节点(类可能会重载 delete 操作符, build_op_delete_call 将找出适合使用的操作符)。
然后在调用 3001 行的 build_dtor_call 之前,首先要设置好参数 auto_delete 。看到在 2973 及 2990 行,它被设置为 sfk_complete_destructor ,表示 build_dtor_call 要找出一个析构函数来析构包括虚拟基类的整个对象。而同时 do_delete 指向这个 delete 表达式。注意到断言 TYPE_GETS_REG_DELETE 只是显示在派生树中是否有重载的 delete 操作符;它可能在基类中定义,而如果这个 delete 操作符被声明为 private ,则它不能从其派生类中访问,因此在 2997 行, build_op_delete_call 将执行这个检查。
回到 cxx_maybe_build_cleanup ,如果一个类包含虚拟基类但没有定义析构函数,那么它不会析构这些虚拟基类。但对虚拟基类的析构是必须的,因此对应的析构操作由 11243 行的 build_vbase_delete 来产生。
3108 tree
3109 build_vbase_delete (tree type, tree decl) in init.c
3110 {
3111 tree vbases = CLASSTYPE_VBASECLASSES (type);
3112 tree result;
3113 tree addr = build_unary_op (ADDR_EXPR, decl, 0);
3114
3115 my_friendly_assert (addr != error_mark_node, 222);
3116
3117 for (result = convert_to_void (integer_zero_node, NULL);
3118 vbases; vbases = TREE_CHAIN (vbases))
3119 {
3120 tree base_addr = convert_force
3121 (build_pointer_type (BINFO_TYPE (TREE_VALUE (vbases))), addr, 0);
3122 tree base_delete = build_delete
3123 TREE_TYPE (base_addr), base_addr, sfk_base_destructor,
3124 LOOKUP_NORMAL|LOOKUP_DESTRUCTOR, 0);
3125
3126 result = build_compound_expr (result, base_delete);
3127 }
3128 return result;
3129 }
再一次看到,如果我们不定义析构函数,或析构函数是平凡的,不会产生任何东西,除了回收内存的行动。而如果产生了某些代码,它被构建入一个 CLEANUP_STMT ,并插入正在构建的语句树中。
1164 void
1165 finish_decl_cleanup (tree decl, tree cleanup) in semantics.c
1166 {
1167 add_stmt (build_stmt (CLEANUP_STMT, decl, cleanup));
1168 }