在 5350 行,当我们正在处理一个成员函数时, current_class_ptr 是用于‘ this ’指针的 PARM_DECL 。看到在下面,仅当 instance 的类型是可以明确知道的情形,才返回非空值。与 5281 行的条件相比, 5281 行的条件表示一个指针,而在 C++ 里,它可以是该类层次中不同的类型,以提供多态。因此作为结果,它返回 NULL_TREE 。
5275 static tree
5276 fixed_type_or_null (tree instance, int* nonnull, int* cdtorp) in class.c
5277 {
5278 switch (TREE_CODE (instance))
5279 {
5280 case INDIRECT_REF:
5281 if (POINTER_TYPE_P (TREE_TYPE (instance)))
5282 return NULL_TREE;
5283 else
5284 return fixed_type_or_null (TREE_OPERAND (instance, 0),
5285 nonnull, cdtorp);
5286
5287 case CALL_EXPR:
5288 /* This is a call to a constructor, hence it's never zero. */
5289 if (TREE_HAS_CONSTRUCTOR (instance))
5290 {
5291 if (nonnull)
5292 *nonnull = 1;
5293 return TREE_TYPE (instance);
5294 }
5295 return NULL_TREE;
5296
5297 case SAVE_EXPR:
5298 /* This is a call to a constructor, hence it's never zero. */
5299 if (TREE_HAS_CONSTRUCTOR (instance))
5300 {
5301 if (nonnull)
5302 *nonnull = 1;
5303 return TREE_TYPE (instance);
5304 }
5305 return fixed_type_or_null (TREE_OPERAND (instance, 0), nonnull, cdtorp);
5306
5307 case RTL_EXPR:
5308 return NULL_TREE;
5309
5310 case PLUS_EXPR:
5311 case MINUS_EXPR:
5312 if (TREE_CODE (TREE_OPERAND (instance, 0)) == ADDR_EXPR)
5313 return fixed_type_or_null (TREE_OPERAND (instance, 0), nonnull, cdtorp);
5314 if (TREE_CODE (TREE_OPERAND (instance, 1)) == INTEGER_CST)
5315 /* Propagate nonnull. */
5316 return fixed_type_or_null (TREE_OPERAND (instance, 0), nonnull, cdtorp);
5317 return NULL_TREE;
5318
5319 case NOP_EXPR:
5320 case CONVERT_EXPR:
5321 return fixed_type_or_null (TREE_OPERAND (instance, 0), nonnull, cdtorp);
5322
5323 case ADDR_EXPR:
5324 if (nonnull)
5325 *nonnull = 1;
5326 return fixed_type_or_null (TREE_OPERAND (instance, 0), nonnull, cdtorp);
5327
5328 case COMPONENT_REF:
5329 return fixed_type_or_null (TREE_OPERAND (instance, 1), nonnull, cdtorp);
5330
5331 case VAR_DECL:
5332 case FIELD_DECL:
5333 if (TREE_CODE (TREE_TYPE (instance)) == ARRAY_TYPE
5334 && IS_AGGR_TYPE (TREE_TYPE (TREE_TYPE (instance))))
5335 {
5336 if (nonnull)
5337 *nonnull = 1;
5338 return TREE_TYPE (TREE_TYPE (instance));
5339 }
5340 /* fall through... */
5341 case TARGET_EXPR:
5342 case PARM_DECL:
5343 case RESULT_DECL:
5344 if (IS_AGGR_TYPE (TREE_TYPE (instance)))
5345 {
5346 if (nonnull)
5347 *nonnull = 1;
5348 return TREE_TYPE (instance);
5349 }
5350 else if (instance == current_class_ptr )
5351 {
5352 if (nonnull)
5353 *nonnull = 1;
5354
5355 /* if we're in a ctor or dtor, we know our type. */
5356 if (DECL_LANG_SPECIFIC (current_function_decl )
5357 && (DECL_CONSTRUCTOR_P (current_function_decl )
5358 || DECL_DESTRUCTOR_P (current_function_decl )))
5359 {
5360 if (cdtorp)
5361 *cdtorp = 1;
5362 return TREE_TYPE (TREE_TYPE (instance));
5363 }
5364 }
5365 else if (TREE_CODE (TREE_TYPE (instance)) == REFERENCE_TYPE)
5366 {
5367 /* Reference variables should be references to objects. */
5368 if (nonnull)
5369 *nonnull = 1;
5370
5371 /* DECL_VAR_MARKED_P is used to prevent recursion; a
5372 variable's initializer may refer to the variable
5373 itself. */
5374 if (TREE_CODE (instance) == VAR_DECL
5375 && DECL_INITIAL (instance)
5376 && !DECL_VAR_MARKED_P (instance))
5377 {
5378 tree type;
5379 DECL_VAR_MARKED_P (instance) = 1;
5380 type = fixed_type_or_null (DECL_INITIAL (instance),
5381 nonnull, cdtorp);
5382 DECL_VAR_MARKED_P (instance) = 0;
5383 return type;
5384 }
5385 }
5386 return NULL_TREE;
5387
5388 default :
5389 return NULL_TREE;
5390 }
5391 }
来到这里, fixed_type_p 是 0 ,如果 expr 的类型是不确定的(可能使用了多态);或者是 -1 ,如果 expr 是构造函数或析构函数;否则就是 1 ,因为类型能确定。而 v_binfo 不是 NULL ,如果涉及了虚拟基类。
build_base_path (continue)
293 if (want_pointer && !nonnull)
294 null_test = build (EQ_EXPR, boolean_type_node, expr, integer_zero_node);
295
296 offset = BINFO_OFFSET (binfo);
如果没有发现虚拟基类, 296 行得到的 offset 就是所期望的。下面的代码产生了表达式(以例 4 为例的伪代码形式):
*(&b + offset (A));
而如果 b 一开始是一个指针,将产生下面的代码来防护 NULL 指针(以例 5 为例):
pa? (pa + offset (A)) : 0;
build_base_path (continue)
350 target_type = code == PLUS_EXPR ? BINFO_TYPE (binfo) : BINFO_TYPE (d_binfo);
351
352 target_type = cp_build_qualified_type
353 (target_type, cp_type_quals (TREE_TYPE (TREE_TYPE (expr))));
354 ptr_target_type = build_pointer_type (target_type);
355 if (want_pointer)
356 target_type = ptr_target_type;
357
358 expr = build1 (NOP_EXPR, ptr_target_type, expr);
359
360 if (!integer_zerop (offset))
361 expr = build (code, ptr_target_type, expr, offset);
362 else
363 null_test = NULL;
364
365 if (!want_pointer)
366 expr = build_indirect_ref (expr, NULL);
367
368 if (null_test)
369 expr = build (COND_EXPR, target_type, null_test,
370 build1 (NOP_EXPR, target_type, integer_zero_node),
371 expr);
372
373 return expr;
374 }
不过,如果转换涉及虚拟基类,并且 expr 的动态类型与静态类型不相同,在 296 行得到 offset 只是开始。
build_base_path (continue)
298 if (v_binfo && fixed_type_p <= 0)
299 {
300 /* Going via virtual base V_BINFO. We need the static offset
301 from V_BINFO to BINFO, and the dynamic offset from D_BINFO to
302 V_BINFO. That offset is an entry in D_BINFO's vtable. */
303 tree v_offset;
304
305 if (fixed_type_p < 0 && in_base_initializer )
306 {
307 /* In a base member initializer, we cannot rely on
308 the vtable being set up. We have to use the vtt_parm. */
309 tree derived = BINFO_INHERITANCE_CHAIN (v_binfo);
310
311 v_offset = build (PLUS_EXPR, TREE_TYPE (current_vtt_parm ),
312 current_vtt_parm , BINFO_VPTR_INDEX (derived));
313
314 v_offset = build1 (INDIRECT_REF,
315 TREE_TYPE (TYPE_VFIELD (BINFO_TYPE (derived))),
316 v_offset);
317
318 }
319 else
320 v_offset = build_vfield_ref (build_indirect_ref (expr, NULL),
321 TREE_TYPE (TREE_TYPE (expr)));
322
323 v_offset = build (PLUS_EXPR, TREE_TYPE (v_offset),
324 v_offset, BINFO_VPTR_FIELD (v_binfo));
325 v_offset = build1 (NOP_EXPR,
326 build_pointer_type (ptrdiff_type_node ),
327 v_offset);
328 v_offset = build_indirect_ref (v_offset, NULL);
329
330 offset = convert_to_integer (ptrdiff_type_node ,
331 size_diffop (offset,
332 BINFO_OFFSET (v_binfo)));
333
334 if (!integer_zerop (offset))
335 v_offset = build (code, ptrdiff_type_node , v_offset, offset);
336
337 if (fixed_type_p < 0)
338 /* Negative fixed_type_p means this is a constructor or destructor;
339 virtual base layout is fixed in in-charge [cd]tors, but not in
340 base [cd]tors. */
341 offset = build (COND_EXPR, ptrdiff_type_node ,
342 build (EQ_EXPR, boolean_type_node,
343 current_in_charge_parm , integer_zero_node),
344 v_offset,
345 BINFO_OFFSET (binfo));
346 else
347 offset = v_offset;
348 }
如果中间有虚拟基类 , 那么必须使用 vtable 来进行派生类与基类间的转换。上面在 305 行的 in_base_initializer , 如果正在处理一个基类初始值 , 是非 0 值。在这个情形下还没有创建派生类的 vtable ,因此需要占位符 current_vtt_parm 来表示涉及 vtable 。
函数 build_indirect_ref 为指针类型或引用类型构建 INDIRECT_REF 。例如,如果 p 是一个指针,那么这个 INDIRECT_REF 节点代表表达式“ *p ”。
2028 tree
2029 build_indirect_ref (tree ptr, const char *errorstring) in typeck.c
2030 {
2031 tree pointer, type;
2032
2033 if (ptr == error_mark_node)
2034 return error_mark_node;
2035
2036 if (ptr == current_class_ptr )
2037 return current_class_ref ;
2038
2039 pointer = (TREE_CODE (TREE_TYPE (ptr)) == REFERENCE_TYPE
2040 ? ptr : decay_conversion (ptr));
2041 type = TREE_TYPE (pointer);
2042
2043 if (TYPE_PTR_P (type) || TREE_CODE (type) == REFERENCE_TYPE)
2044 {
2045 /* [expr.unary.op]
2046
2047 If the type of the expression is "pointer to T," the type
2048 of the result is "T."
2049
2050 We must use the canonical variant because certain parts of
2051 the back end, like fold, do pointer comparisons between
2052 types. */
2053 tree t = canonical_type_variant (TREE_TYPE (type));
2054
2055 if (VOID_TYPE_P (t))
2056 {
2057 /* A pointer to incomplete type (other than cv void) can be
2058 dereferenced [expr.unary.op]/1 */
2059 error ("`%T' is not a pointer-to-object type", type);
2060 return error_mark_node;
2061 }
2062 else if (TREE_CODE (pointer) == ADDR_EXPR
2063 && same_type_p (t, TREE_TYPE (TREE_OPERAND (pointer, 0))))
2064 /* The POINTER was something like `&x'. We simplify `*&x' to
2065 `x'. */
2066 return TREE_OPERAND (pointer, 0);
2067 else
2068 {
2069 tree ref = build1 (INDIRECT_REF, t, pointer);
2070
2071 /* We *must* set TREE_READONLY when dereferencing a pointer to const,
2072 so that we get the proper error message if the result is used
2073 to assign to. Also, &* is supposed to be a no-op. */
2074 TREE_READONLY (ref) = CP_TYPE_CONST_P (t);
2075 TREE_THIS_VOLATILE (ref) = CP_TYPE_VOLATILE_P (t);
2076 TREE_SIDE_EFFECTS (ref)
2077 = (TREE_THIS_VOLATILE (ref) || TREE_SIDE_EFFECTS (pointer));
2078 return ref;
2079 }
2080 }
2081 /* `pointer' won't be an error_mark_node if we were given a
2082 pointer to member, so it's cool to check for this here. */
2083 else if (TYPE_PTR_TO_MEMBER_P (type))
2084 error ("invalid use of `%s' on pointer to member", errorstring);
2085 else if (pointer != error_mark_node)
2086 {
2087 if (errorstring)
2088 error ("invalid type argument of `%s'", errorstring);
2089 else
2090 error ("invalid type argument");
2091 }
2092 return error_mark_node;
2093 }
我们已经看到 TYPE_VFIELD 是由编译器为包含 vtable 的类产生的 “ 人造 ” 域。现在它也作为一个占位符 , 后面它将为真正的地址所替代。
115 tree
116 build_vfield_ref (tree datum, tree type) in call.c
117 {
118 if (datum == error_mark_node)
119 return error_mark_node;
120
121 if (TREE_CODE (TREE_TYPE (datum)) == REFERENCE_TYPE)
122 datum = convert_from_reference (datum);
123
124 if (TYPE_BASE_CONVS_MAY_REQUIRE_CODE_P (type)
125 && !same_type_ignoring_top_level_qualifiers_p (TREE_TYPE (datum), type))
126 datum = convert_to_base (datum, type, /*check_access=*/ false);
127
128 return build (COMPONENT_REF, TREE_TYPE (TYPE_VFIELD (type)),
129 datum, TYPE_VFIELD (type));
130 }
记得在 324 行的 BINFO_VPTR_FIELD 为虚拟基类设置,其中是一个 INTEGER_CST ,它是 vtable 中一个项的索引,其中记录了从派生类的基址到该虚拟基类的偏移量(为什么不是 BINFO_OFFSET (vbase) ?因为 C++ 标准要求通过 BINFO_VPTR_FIELD 访问虚拟基类)。
注意到如果 binfo 被 v_binfo 继承,那么在 330 行的 offset 得到由下图所示的 2 个基类间的相对偏移量(红色指针 是在 330 行得到的 offset )。
然后 334 行引入了一个细微的优化,注意到作为 MINUS_EXPR 的 code ,连同非空的 v_binfo 是不被允许的,它在 280 行给出错误消息。因此在 335 行,在右手边的 v_offset 通过 vtable 指向该虚拟基类的 binfo ,并加上 330 行得到的 offset ,在左手边的 v_offset 因而指向感兴趣的 binfo 。接下来 offset 被相应地更新,来记录到最后派生类的偏移量。
对于我们的例 5 ,最后产生的 expr 应该是:
(pa + offset (A))? (pa + offset (A)): 0;
回到 get_member_function_from_ptrfunc , instance_ptr 现在是上面由 build_base_path 返回的语句。考虑下面的例子,它给出结果“ C::f ”(注意到在类 C 中, B1 , B2 及 A 的 f 的 vtable 项都被 C::f 使用合适的 thunk 覆盖,它们在这里给我们作出多态的保证):
class A { public : 例 - 6
virtual A* f () { printf ("A::f/n"); return 0; }
virtual A* f1 () { printf ("A::f1/n"); return 0; }
virtual ~A() {}
};
class B1 : virtual public A { public : virtual B1* f1 () { printf ("B1::f1/n"); return 0; } };
class B2 : virtual public A {};
class C: public B1, public B2 { public : virtual C* f () { printf ("C::f/n"); return 0; } };
int main() {
B2 *pc = new C; // no matter declare pc as A*, B1*, B2*, or C*, get same result, as // they all derive from A, and pfn is declared as within A
A* (A::*pfn) () = &B2::f; // same result, use either B1, B2, A, or C at rhs
(pc->*pfn)();
delete pc;
return 0;
}
对于这个例子, instance_ptr 保存了把 pa 从 B2* 调整到 A* 的语句(“ B2 *pc = new C; ”携带一个已经从 C* 转换到 B2* 的指针);而下面在 2357 行得到的 delta 记录了由语句“ A* (A::*pfn) () = &B2::f; ”确定的偏移量(它是 0 因为该语句表示一个从 A 到 A 的没有意义的调整量)。那么通过表达式:“ instance_ptr + delta ”,找出定义了由该方法指针指定的函数的基类;因此 idx 就是用于该虚函数(或对于非虚函数,转换为 vtable 类型的函数地址)的 vtable 的索引。
get_member_function_from_ptrfunc (continue)
2383 /* ...and then the delta in the PMF. */
2384 instance_ptr = build (PLUS_EXPR, TREE_TYPE (instance_ptr),
2385 instance_ptr, delta);
2386
2387 /* Hand back the adjusted 'this' argument to our caller. */
2388 *instance_ptrptr = instance_ptr;
2389
2390 /* Next extract the vtable pointer from the object. */
2391 vtbl = build1 (NOP_EXPR, build_pointer_type (vtbl_ptr_type_node),
2392 instance_ptr);
2393 vtbl = build_indirect_ref (vtbl, NULL);
2394
2395 /* Finally, extract the function pointer from the vtable. */
2396 e2 = fold (build (PLUS_EXPR, TREE_TYPE (vtbl), vtbl, idx));
2397 e2 = build_indirect_ref (e2, NULL);
2398 TREE_CONSTANT (e2) = 1;
2399
2400 /* When using function descriptors, the address of the
2401 vtable entry is treated as a function pointer. */
2402 if (TARGET_VTABLE_USES_DESCRIPTORS)
2403 e2 = build1 (NOP_EXPR, TREE_TYPE (e2),
2404 build_unary_op (ADDR_EXPR, e2, /*noconvert=*/ 1));
2405
2406 TREE_TYPE (e2) = TREE_TYPE (e3);
2407 e1 = build_conditional_expr (e1, e2, e3);
2408
2409 /* Make sure this doesn't get evaluated first inside one of the
2410 branches of the COND_EXPR. */
2411 if (instance_save_expr)
2412 e1 = build (COMPOUND_EXPR, TREE_TYPE (e1),
2413 instance_save_expr, e1);
2414
2415 function = e1;
2416 }
2417 return function;
2418 }
在类布局一节中,我们已经看到 vptr 总是放置在该类的开头。因此对于例 5 ,所产生的代码片段,看起来就像下面:
idx = (vtable_index_type) pfn; // pfn returned by PFN_FROM_PTRMEMFUNC
instance_ptr = instance_ptr? instance_ptr + offset (A): 0;
(idx & 1)? *(((vtbl_ptr_type_node) instance_ptr) + (idx-1)): pfn;
这时,对于一个方法指针,我们已经知道所期待的是哪个函数,而且指出该函数位置的表达式已经准备好了,并返回给 build_addr_func ,然后立即返回给 build_function_call 。注意到下面的 function 现在保存了定位该函数的这个表达式,并且其类型是该函数的指针。
build_function_call (continue)
2464 if (function == error_mark_node)
2465 return error_mark_node;
2466
2467 fntype = TREE_TYPE (function);
2468
2469 if (TYPE_PTRMEMFUNC_P (fntype))
2470 {
2471 error ("must use .* or ->* to call pointer-to-member function in `%E (...)'",
2472 original);
2473 return error_mark_node;
2474 }
2475
2476 is_method = (TREE_CODE (fntype) == POINTER_TYPE
2477 && TREE_CODE (TREE_TYPE (fntype)) == METHOD_TYPE);
2478
2479 if (!((TREE_CODE (fntype) == POINTER_TYPE
2480 && TREE_CODE (TREE_TYPE (fntype)) == FUNCTION_TYPE)
2481 || is_method
2482 || TREE_CODE (function) == TEMPLATE_ID_EXPR))
2483 {
2484 error ("`%E' cannot be used as a function", original);
2485 return error_mark_node;
2486 }
2487
2488 /* fntype now gets the type of function pointed to. */
2489 fntype = TREE_TYPE (fntype);
2490
2491 /* Convert the parameters to the types declared in the
2492 function prototype, or apply default promotions. */
2493
2494 coerced_params = convert_arguments (TYPE_ARG_TYPES (fntype),
2495 params, fndecl, LOOKUP_NORMAL);
2496 if (coerced_params == error_mark_node)
2497 return error_mark_node;
2498
2499 /* Check for errors in format strings. */
2500
2501 if (warn_format )
2502 check_function_format (NULL, TYPE_ATTRIBUTES (fntype), coerced_params);
2503
2504 /* Recognize certain built-in functions so we can make tree-codes
2505 other than CALL_EXPR. We do this when it enables fold-const.c
2506 to do something useful. */
2507
2508 if (TREE_CODE (function) == ADDR_EXPR
2509 && TREE_CODE (TREE_OPERAND (function, 0)) == FUNCTION_DECL
2510 && DECL_BUILT_IN (TREE_OPERAND (function, 0)))
2511 {
2512 result = expand_tree_builtin (TREE_OPERAND (function, 0),
2513 params, coerced_params);
2514 if (result)
2515 return result;
2516 }
2517
2518 return build_cxx_call (function, params, coerced_params);
2519 }
因此在 2489 行的 fntype 指向所使用的函数的类型。然后需要根据参数声明,为实参执行转换。