At line 5350 current_class_ptr is the PARM_DECL for the `this' pointer, when we're processing a member function. See that in below only the type of instance can be known for sure it returns non-null. Compared with condition at line 5281, which indicates a pointer, and in C++, it can be variant types in the class hierarchy to support polymorphism. So it has to return NULL_TREE as result.
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 }
Arriving here, fixed_type_p is 0 if type of expr is unknown for certainty (polymorphism may be in used), or is -1 if expr is constructor or destructor, otherwise is 1 as type is known for certainty. And v_binfo is not NULL if there is virtual base involved.
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);
If no vritual base found, offset gotten at line 296 is all wanted. Code in below then generates the expression (taking example 4 as instance in pseudo code):
*(&b + offset (A));
And if b originally is a pointer, it will generate code to guard NULL pointer too as below (taking example 5 as instance):
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 }
However, if the conversion involves virtual base and expr ’s dynamic type isn’t the same as static type, offset gotten at line 296 is just the beginning.
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 }
If there is virtual base between and it must use vtable to do the conversion between the derived and the base, above at line 305 in _base_initializer is nonzero if it is handling a base initializer, for which case vtable is not created. So it needs use the placeholder current_vtt_parm to indicate to involvement of the vtable.
Function build_indirect_ref builds INDIRECT_REF for pointer type or reference type. For example, if p is a pointer, then the INDIRECT_REF node stands for the expression “*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 }
As we have seen TYPE_VFIELD is the artificial field generated by the compiler for class containing vtable. Now it also acts as a placeholder, it will be replaced with the real address of vtable later.
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 }
Remember BINFO_VPTR_FIELD at line 324 is set for virtual base, in which is an INTEGER_CST indicating the index of the entry in the vtable recording the offset of the virtual base from the base of the derived (why not BINFO_OFFSET (vbase)? because C++ standard requires accessing virtual base by help of BINFO_VPTR_FIELD).
Note that if binfo is derived by v_binfo , then offset at line 330 gets the relative offset between these two bases as indicated by below figure (the red arrow is the offset gotten at line 330).
Then line 334 introduces a minor optimizaton, note that code of MINUS_EXPR with v_binfo not NULL is not allowed and error message is given at line 280. So at line 335, v_offset at right hand side refers to the binfo of the virtual base via vtable, and plus offset gotten at line 330, v_offset at left hand side then points to the binfo in interesting. Following offset is updated accordingly to record the offset into the most derived class.
For our example 5, expr generated finally would be:
(pa + offset (A))? (pa + offset (A)): 0;
Back into get_member_function_from_ptrfunc , instance_ptr is now the above statement returned by build_base_path . Consider following example, it gives output “C::f” (note that in class C, vtable entry of f of B1, B2, and A are all overrided as C::f with appropriate thunk, which give us promise here):
class A { public : example - 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;
}
For this example, instance_ptr holds the statement to adjust pa from B2* to A* (“B2 *pc = new C;” carries a pointer converted from C* to B2* already); and delta below gotten at line 2357 records the offset determined by statement “A* (A::*pfn) () = &B2::f;” (it is 0 because the statement indicate a meaningless adjustment from A to A). Then by expression: “instance_ptr + delta”, it locates the base that defining the function specified by the pointer to method; then idx is the index into the vtable for the virtual function (or function address converting in type of vtable for non-virtual function).
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 }
In section of class layout, we have seen that vptr is always placed at the beginning of the class. So the code snippet generated would be like below for example 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;
At this point, for a pointer-to-method, we have known which function is expected, and expression to locate the location of the function is ready and returned to build_addr_func , then immediately to build_function_call . Note that function below now holds the expression for locating the function and its type is the pointer of the 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 }
So fntype at line 2489 refers to the type of the function in used. Then it needs do conversion for the arguments according to the parameters declaration.