Now, we begin a new long offshoot away from our main route. When we write code like: f (a, b); to call function f , the compiler needs work hard to make this invocation workable and in expected way. Below function reveals what compiler will do.
2420 tree
2421 build_function_call (tree function, tree params) in typeck.c
2422 {
2423 tree fntype, fndecl;
2424 tree coerced_params;
2425 tree result;
2426 tree name = NULL_TREE, assembler_name = NULL_TREE;
2427 int is_method;
2428 tree original = function;
2429
2430 /* build_c_cast puts on a NOP_EXPR to make the result not an lvalue.
2431 Strip such NOP_EXPRs, since FUNCTION is used in non-lvalue context. */
2432 if (TREE_CODE (function) == NOP_EXPR
2433 && TREE_TYPE (function) == TREE_TYPE (TREE_OPERAND (function, 0)))
2434 function = TREE_OPERAND (function, 0);
2435
2436 if (TREE_CODE (function) == FUNCTION_DECL)
2437 {
2438 name = DECL_NAME (function);
2439 assembler_name = DECL_ASSEMBLER_NAME (function);
2440
2441 mark_used (function);
2442 fndecl = function;
2443
2444 /* Convert anything with function type to a pointer-to-function. */
2445 if (pedantic && DECL_MAIN_P (function))
2446 pedwarn ("ISO C++ forbids calling `::main' from within program");
2447
2448 /* Differs from default_conversion by not setting TREE_ADDRESSABLE
2449 (because calling an inline function does not mean the function
2450 needs to be separately compiled). */
2451
2452 if (DECL_INLINE (function))
2453 function = inline_conversion (function);
2454 else
2455 function = build_addr_func (function);
2456 }
2457 else
2458 {
2459 fndecl = NULL_TREE;
2460
2461 function = build_addr_func (function);
2462 }
Inline function is expanded at the point of invocation. So it needs not be compiled separately outside the translation-unit; neither mark it as TREE_ADDRESSABLE. And here ADDR_EXPR is generated as result of the call statement.
1464 tree
1465 inline_conversion (tree exp) in typeck.c
1466 {
1467 if (TREE_CODE (exp) == FUNCTION_DECL)
1468 exp = build1 (ADDR_EXPR, build_pointer_type (TREE_TYPE (exp)), exp);
1469
1470 return exp;
1471 }
While inline function accessed via pointer (in the form of x.*p, and for which case x is addressable, so build_address (it requires addressable object as argument) is workable), the accessibility checking is expected; and for calling normal function, it needs decay_conversion to perform conversion of function to pointer.
178 tree
179 build_addr_func (tree function) in tree.c
180 {
181 tree type = TREE_TYPE (function);
182
183 /* We have to do these by hand to avoid real pointer to member
184 functions. */
185 if (TREE_CODE (type) == METHOD_TYPE)
186 {
187 if (TREE_CODE (function) == OFFSET_REF)
188 {
189 tree object = build_address (TREE_OPERAND (function, 0));
190 return get_member_function_from_ptrfunc (&object,
191 TREE_OPERAND (function, 1));
192 }
193 function = build_address (function);
194 }
195 else
196 function = decay_conversion (function);
197
198 return function;
199 }
Above OFFSET_REF is used in two situations:
1. An expression of the form `A::m' where `A' is a class and `m' is a non-static member. In this case, operand 0 will be a TYPE (corresponding to `A') and operand 1 will be a FIELD_DECL, BASELINK, or TEMPLATE_ID_EXPR (corresponding to `m').
The expression is a pointer-to-member if its address is taken, but simply denotes a member of the object if its address isn’t taken.
This form is only used during the parsing phase; once semantic analysis has taken place they are eliminated.
2. An expression of the form `x.*p'. In this case, operand 0 will be an expression corresponding to `x' and operand 1 will be an expression with pointer-to-member type.
Obviously, pointer to method of class complies with the second rule. For non-static method invocation, remember there is an implicit “const this*”, line 189 above, object is the this pointer. Further in C++, there is a magic type – method type [14], for example: “typedef void (C::*Cf) () = &C::f;” Cf is the type of method taking no argument and returning void in C (assuming C is a class). Predicate TYPE_PTRMEMFUNC_P at line 2324 returns true for such type. And at line 2327, TYPE_PTRMEMFUNC_FN_TYPE fetches the associated method type.
2318 tree
2319 get_member_function_from_ptrfunc (tree *instance_ptrptr, tree function) in typeck.c
2320 {
2321 if (TREE_CODE (function) == OFFSET_REF)
2322 function = TREE_OPERAND (function, 1);
2323
2324 if (TYPE_PTRMEMFUNC_P (TREE_TYPE (function)))
2325 {
2326 tree idx, delta, e1, e2, e3, vtbl, basetype;
2327 tree fntype = TYPE_PTRMEMFUNC_FN_TYPE (TREE_TYPE (function));
2328
2329 tree instance_ptr = *instance_ptrptr;
2330 tree instance_save_expr = 0;
2331 if (instance_ptr == error_mark_node)
2332 {
2333 if (TREE_CODE (function) == PTRMEM_CST)
2334 {
2335 /* Extracting the function address from a pmf is only
2336 allowed with -Wno-pmf-conversions. It only works for
2337 pmf constants. */
2338 e1 = build_addr_func (PTRMEM_CST_MEMBER (function));
2339 e1 = convert (fntype, e1);
2340 return e1;
2341 }
2342 else
2343 {
2344 error ("object missing in use of `%E'", function);
2345 return error_mark_node;
2346 }
2347 }
2348
2349 if (TREE_SIDE_EFFECTS (instance_ptr))
2350 instance_ptr = instance_save_expr = save_expr (instance_ptr);
2351
2352 if (TREE_SIDE_EFFECTS (function))
2353 function = save_expr (function);
2354
2355 /* Start by extracting all the information from the PMF itself. */
2356 e3 = PFN_FROM_PTRMEMFUNC (function);
If instance_ptr is error_mark_node , function here should be a PTRMEM_CST (node of pointer-to-member constant), for which PTRMEM_CST_CLASS is the RECORD_TYPE of C and PTRMEM_CST_MEMBER is the *_DECL for f (I can’t think out a normal case that will generate such node, because build_address (189 line) above always successes for instance_ptr unless the node passed in is error_mark_node , which means something wrong in the procedure. And even for the case of converting pointer-to-method to pointer-to-function as the comment mentions, it seems it should not enter here, detail of which can be seen in [6]).
In C++, pointer to member functions (PMFs) are implemented using a wide pointer of sorts to handle all the possible call mechanisms; the PMF needs to store information about how to adjust the ‘this’ pointer, and if the function pointed to is virtual, where to find the vtable, and where in the vtable to look for the member function. If you are using PMFs in an inner loop, you should really reconsider that decision. If that is not an option, you can extract the pointer to the function that would be called for a given object/PMF pair and call it directly inside the inner loop, to save a bit of time. Note that you will still be paying the penalty for the call through a function pointer; on most modern architectures, such a call defeats the branch prediction features of the CPU. This is also true of normal virtual function calls. The syntax for this extension is extern A a; extern int (A::*fp)(); typedef int (*fptr)(A *); fptr p = (fptr)(a.*fp); For PMF constants (i.e. expressions of the form ‘&Klasse::Member’), no object is needed to obtain the address of the function. They can be converted to function pointers directly: fptr p1 = (fptr)(&A::foo); You must specify ‘-Wno-pmf-conversions’ to use this extension. |
While if instance_ptr isn’t error_mark_node , function at line 2356 is the expression with pointer-to-member type, and PFN_FROM_PTRMEMFUNC has below definition.
2563 #define PFN_FROM_PTRMEMFUNC (NODE) pfn_from_ptrmemfunc ((NODE))
5637 tree
5638 pfn_from_ptrmemfunc (tree t) in typeck.c
5639 {
5640 if (TREE_CODE (t) == PTRMEM_CST)
5641 {
5642 tree delta;
5643 tree pfn;
5644
5645 expand_ptrmemfunc_cst (t, &delta, &pfn);
5646 if (pfn)
5647 return pfn;
5648 }
5649
5650 return build_ptrmemfunc_access_expr (t, pfn_identifier );
5651 }
Above function fetches the method referred by the expression of pointer-to-method type or structure of PTRMEM_CST, and it may need adjust this pointer or manipulate vtable appropriately.
First if above argument function takes the form like: “&C::f”, it is a node of PTRMEM_CST. However under the context of building function call, function should not be PTRMEM_CST; and one of senario that function is PTRMEM_CST is the statement like: “void (B::*pfn)() = &B::f;” then expand_ptrmemfunc_cst will be invoked by build_ptrmemfunc to work out __pfn and __delta from the PTRMEM_CST of “&B::f” for the structure (see below) for the pointer-to-method type of “void (B::*)()”.
Anyway, it worths us taking a little time to see how to determine __pfn and __delta fields for the pointer-to-method. Below TYPE_PTRMEMFUNC_OBJECT_TYPE at line 5587 returns `A' for a type like “int (A::*)(double)”.
5574 void
5575 expand_ptrmemfunc_cst (tree cst, tree *delta, tree *pfn) in typeck.c
5576 {
5577 tree type = TREE_TYPE (cst);
5578 tree fn = PTRMEM_CST_MEMBER (cst);
5579 tree ptr_class, fn_class;
5580
5581 my_friendly_assert (TREE_CODE (fn) == FUNCTION_DECL, 0);
5582
5583 /* The class that the function belongs to. */
5584 fn_class = DECL_CONTEXT (fn);
5585
5586 /* The class that we're creating a pointer to member of. */
5587 ptr_class = TYPE_PTRMEMFUNC_OBJECT_TYPE (type);
5588
5589 /* First, calculate the adjustment to the function's class. */
5590 *delta = get_delta_difference (fn_class, ptr_class, /*force=*/ 0);
Considering following example:
class A { public : void f (); };
class B: public A {};
void (B::*pfn) () = &B::f;
For the example, fn_class would be A (from the part “&B::f”) ptr_class would be B (from the part “vod (B::*pfn) ()”) and fn would be f . So it first needs to know the adjustment between the derived class and the base defining the function. In below function argument force if false means not allow reverse conversions (to -> from).
5392 static tree
5393 get_delta_difference (tree from, tree to, int force) in typeck.c
5394 {
5395 tree binfo;
5396 tree virt_binfo;
5397 base_kind kind;
5398
5399 binfo = lookup_base (to, from, ba_check, &kind);
5400 if (kind == bk_inaccessible || kind == bk_ambig)
5401 {
5402 error (" in pointer to member function conversion");
5403 goto error;
5404 }
5405 if (!binfo)
5406 {
5407 if (!force)
5408 {
5409 error_not_base_type (from, to);
5410 error (" in pointer to member conversion");
5411 goto error;
5412 }
5413 binfo = lookup_base (from, to, ba_check, &kind);
5414 if (!binfo)
5415 goto error;
5416 virt_binfo = binfo_from_vbase (binfo);
5417 if (virt_binfo)
5418 {
5419 /* This is a reinterpret cast, we choose to do nothing. */
5420 warning ("pointer to member cast via virtual base `%T'",
5421 BINFO_TYPE (virt_binfo));
5422 goto error;
5423 }
5424 return fold (convert_to_integer (ptrdiff_type_node ,
5425 size_diffop (size_zero_node,
5426 BINFO_OFFSET (binfo))));
5427 }
5428
5429 virt_binfo = binfo_from_vbase (binfo);
5430 if (!virt_binfo)
5431 return fold (convert_to_integer (ptrdiff_type_node , BINFO_OFFSET (binfo)));
5432
5433 /* This is a reinterpret cast, we choose to do nothing. */
5434 if (force)
5435 warning ("pointer to member cast via virtual base `%T'",
5436 BINFO_TYPE (virt_binfo));
5437 else
5438 error ("pointer to member conversion via virtual base `%T'",
5439 BINFO_TYPE (virt_binfo));
5440
5441 error:
5442 return fold (convert_to_integer(ptrdiff_type_node , integer_zero_node));
5443 }
Note that argument from and to must have inheritage relation. What’s more, if the casting involves virtual base, it is the semantics only can be handled by reinterpret_cast, should be ignored here. And see the internal object ptrdiff_type_node corresponds to ptrdiff_t we often see in C/C++ program. Considering the example:
class base { public : void fb() {} virtual ~base() {} };
class A : public base {};
class B1 : virtual public A {};
class B2 : virtual public A {};
class C: public B1, public B2 {};
int main() {
C c;
void (C::*pfn) () = &base::fb;
(c.*pfn)();
return 0;
}
Statement “PFN pfn = &base::fb;” triggers the error “error: pointer to member conversion via virtual base ‘A’ ”. But if PFN is defined as “typedef void (base::*PFN) ();”, it will be OK.
Below binfo_from_vbase checks if specified binfo is derived by virtual base.
2525 tree
2526 binfo_from_vbase (tree binfo) in search.c
2527 {
2528 for (; binfo; binfo = BINFO_INHERITANCE_CHAIN (binfo))
2529 {
2530 if (TREE_VIA_VIRTUAL (binfo))
2531 return binfo;
2532 }
2533 return NULL_TREE;
2534 }
If fn is not a virtual function, just converts the expression fn into the type of the pointer-to-method. In internal, the type of pointer-to-method looks like:
struct {
__P __pfn;
ptrdiff_t __delta;
};
If __pfn is NULL, it is a NULL pointer-to-method. (Because the vtable is always the first thing in the object, we don't need its offset.) If the function is virtual, then PFN is one plus twice the index into the vtable; otherwise, it is just a pointer to the function (Unfortunately, using the lowest bit of PFN doesn't work in architectures that don't impose alignment requirements on function addresses, or that use the lowest bit to tell one ISA from another, for example. For such architectures, we use the lowest bit of DELTA instead of the lowest bit of the PFN, and DELTA will be multiplied by 2).
So conversion at line 5593 turns the address of the method specified by fn into the POINTER_TYPE of the corresponding METHOD_TYPE. And delta will be the adjustment for the this pointer for the method.
And if fn is a virtual function, considering below examples:
class A { virtual void f () {} }; example - 1
class B: public A {};
void (B::*pfn) () = &B::f;
class A { virtual void f () {} }; example - 2
class B: public A { virtual void f () {} };
void (B::*pfn) () = &B::f;
class A { public : example - 3
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 { public : virtual B2* f () { printf ("B2::f/n"); return 0; } };
class C: public B1, public B2 {};
int main() {
C c;
B2* (B2::*pfn) () = &C::f;
(c.*pfn)();
return 0;
}
Note that get_delta_difference above gets the offset between the class specified by the PTRMEM_CST and the class defining the method. In above example 1, the class specified by PTRMEM_CST is B, and the class defining f is A; and in example 2, these are both Bs. Then with knowledge of class layout in previous section, obviously, for both examples, class A always is laid out at offset zero in class B; while for example 3, however delta records the offset from B2 to C.
See below orig_class is the same as fn_class , so binfo_or_else at line 560 always successes and returns the binfo of the base type (see not the base part in derived class); BINFO_OFFSET (binfo) at line 5602 is always 0. And delta at line 5601 keeps the offset gotten by get_delta_difference .
Remember DECL_VINDEX at line 5607 is used by virtual function to record its index into the vtable. So pfn at line 5608 is the real offset into the vtable, and it is interesting to see that this pfn is cast into type of pointer-to-method at line 5629 (of course, it is not the final address).
expand_ptrmemfunc_cst (continue)
5592 if (!DECL_VIRTUAL_P (fn))
5593 *pfn = convert (TYPE_PTRMEMFUNC_FN_TYPE (type), build_addr_func (fn));
5594 else
5595 {
5596 /* If we're dealing with a virtual function, we have to adjust 'this'
5597 again, to point to the base which provides the vtable entry for
5598 fn; the call will do the opposite adjustment. */
5599 tree orig_class = DECL_CONTEXT (fn);
5600 tree binfo = binfo_or_else (orig_class, fn_class);
5601 *delta = fold (build (PLUS_EXPR, TREE_TYPE (*delta),
5602 *delta, BINFO_OFFSET (binfo)));
5603
5604 /* We set PFN to the vtable offset at which the function can be
5605 found, plus one (unless ptrmemfunc_vbit_in_delta, in which
5606 case delta is shifted left, and then incremented). */
5607 *pfn = DECL_VINDEX (fn);
5608 *pfn = fold (build (MULT_EXPR, integer_type_node, *pfn,
5609 TYPE_SIZE_UNIT (vtable_entry_type )));
5610
5611 switch (TARGET_PTRMEMFUNC_VBIT_LOCATION)
5612 {
5613 case ptrmemfunc_vbit_in_pfn:
5614 *pfn = fold (build (PLUS_EXPR, integer_type_node, *pfn,
5615 integer_one_node));
5616 break ;
5617
5618 case ptrmemfunc_vbit_in_delta:
5619 *delta = fold (build (LSHIFT_EXPR, TREE_TYPE (*delta),
5620 *delta, integer_one_node));
5621 *delta = fold (build (PLUS_EXPR, TREE_TYPE (*delta),
5622 *delta, integer_one_node));
5623 break ;
5624
5625 default :
5626 abort ();
5627 }
5628
5629 *pfn = fold (build1 (NOP_EXPR, TYPE_PTRMEMFUNC_FN_TYPE (type),
5630 *pfn));
5631 }
5632 }
TARGET_PTRMEMFUNC_VBIT_LOCATION tells where to store the bit inidicating pointer-to-method of virtual function. It may be the lowest bit in the function address; or it may be the lowest bit in this adjustment depends on if function address has enough alignment or not. For x86 chip, it uses the lowest bit in function address. So pfn at line 5629 in fact is the expression: “vindex * sizeof (vtable entry) + 1” (In the other case, delta would be “BINFO_OFFSET (base) * 2 + 1” with extra space to hold the virtual bit).
Not shown here, the delta and pfn gotten here would be set in the structure for the pointer-to-method given above.
59 tree
60 binfo_or_else (tree base, tree type) in typeck2.c
61 {
62 tree binfo = lookup_base (type, base, ba_ignore, NULL);
63
64 if (binfo == error_mark_node)
65 return NULL_TREE;
66 else if (!binfo)
67 error_not_base_type (base, type);
68 return binfo;
69 }
As we mentioning above, under context of building function call, pfn must be an instance of type of pointer-to-method, and for being a correct function call, it must have been initialized by certain PTRMEM_CST as above showing.
Thus build_ptrmemfunc_access_expr helps to fetch the fields mentioned above with argument member_name specifies which one. So at line 5650 in pfn_from_ptrmemfunc , it fetches field of __pfn by pfn_identifier .
2031 tree
2032 build_ptrmemfunc_access_expr (tree ptrmem, tree member_name) in typeck.c
2033 {
2034 tree ptrmem_type;
2035 tree member;
2036 tree member_type;
2037
2038 /* This code is a stripped down version of
2039 build_class_member_access_expr. It does not work to use that
2040 routine directly because it expects the object to be of class
2041 type. */
2042 ptrmem_type = TREE_TYPE (ptrmem);
2043 my_friendly_assert (TYPE_PTRMEMFUNC_P (ptrmem_type), 20020804);
2044 member = lookup_member (ptrmem_type, member_name, /*protect=*/ 0,
2045 /*want_type=*/ false);
2046 member_type = cp_build_qualified_type (TREE_TYPE (member),
2047 cp_type_quals (ptrmem_type));
2048 return fold (build (COMPONENT_REF, member_type, ptrmem, member));
2049 }
Then e3 above at line 2356 as we have seen is the expression concerning the function address (pointer of non-virtual function, or vtable index for virtual function). Next, it fetches the field of __delta into delta .
And for x86 chip, e1 at line 2362 is expression to test if the lowest bit of __pfn is 1 to see if the pointer points virtual function. Then idx at line 2363 is updated to the real offset to the vtable in case of virtual function.
get_member_function_from_ptrfunc (continue)
2357 delta = build_ptrmemfunc_access_expr (function, delta_identifier );
2358 idx = build1 (NOP_EXPR, vtable_index_type , e3);
2359 switch (TARGET_PTRMEMFUNC_VBIT_LOCATION)
2360 {
2361 case ptrmemfunc_vbit_in_pfn:
2362 e1 = cp_build_binary_op (BIT_AND_EXPR, idx, integer_one_node);
2363 idx = cp_build_binary_op (MINUS_EXPR, idx, integer_one_node);
2364 break ;
2365
2366 case ptrmemfunc_vbit_in_delta:
2367 e1 = cp_build_binary_op (BIT_AND_EXPR, delta, integer_one_node);
2368 delta = cp_build_binary_op (RSHIFT_EXPR, delta, integer_one_node);
2369 break ;
2370
2371 default :
2372 abort ();
2373 }
2374
2375 /* Convert down to the right base before using the instance. First
2376 use the type... */
2377 basetype = TYPE_METHOD_BASETYPE (TREE_TYPE (fntype));
2378 basetype = lookup_base (TREE_TYPE (TREE_TYPE (instance_ptr)),
2379 basetype, ba_check, NULL);
2380 instance_ptr = build_base_path (PLUS_EXPR, instance_ptr, basetype, 1);
2381 if (instance_ptr == error_mark_node)
2382 return error_mark_node;
Note that instance_ptr is a pointer, so TREE_TYPE of TREE_TYPE of instance_ptr returns the RECORD_TYPE of the object. And TYPE_METHOD_BASETYPE returns the base the function declared within. See that type associated with instance_ptr and type associated with the function must have derivation relationship.
Function build_base_path converts to or from a base subobject. Argument expr is an expression of type `A' or `A*', an expression of type `B' or `B*' is returned. To convert A to a base B, code is PLUS_EXPR and binfo is the binfo for the B base instance within A. To convert base A to derived B, code is MINUS_EXPR and binfo is the binfo for the A instance within B. In latter case, A must not be a morally virtual base of B. Argument nonnull is true if expr is known to be non-NULL (this is only needed when expr is of pointer type). CV qualifiers are preserved from expr . Considering following example:
class A { public : void fa (); }; example - 4
class B: public A { public : void fb(); };
class C:: public B {};
void (B::*pfn) = &B::f;
C c;
(c.*pfn) ();
For the example, fntype is the type of pfn , so basetype at line 2377 is B (the type the pointer-to-method bound with); while instance_ptr is of type C. It needs adjust instance_ptr to the base of B to execute statement “(c.*pfn)();”
Below build_base_path builds the expression to do such conversion. In the function instance_ptr is passed as argument expr , which is a pointer, so want_pointer below is nonzero. And of course argument nonnull is not zero. Then v_binfo refers to the closest virtual derived class of binfo , and d_binfo records the most derived class by FOR block at line 261. Note that binfo is gotten from the inheritance tree rooted by type of expr , assertion at line 272 must be satisfied for code of PLUS_EXPR.
242 tree
243 build_base_path (enum tree_code code, in class.c
244 tree expr,
245 tree binfo,
246 int nonnull)
247 {
248 tree v_binfo = NULL_TREE;
249 tree d_binfo = NULL_TREE;
250 tree probe;
251 tree offset;
252 tree target_type;
253 tree null_test = NULL;
254 tree ptr_target_type;
255 int fixed_type_p;
256 int want_pointer = TREE_CODE (TREE_TYPE (expr)) == POINTER_TYPE;
257
258 if (expr == error_mark_node || binfo == error_mark_node || !binfo)
259 return error_mark_node;
260
261 for (probe = binfo; probe; probe = BINFO_INHERITANCE_CHAIN (probe))
262 {
263 d_binfo = probe;
264 if (!v_binfo && TREE_VIA_VIRTUAL (probe))
265 v_binfo = probe;
266 }
267
268 probe = TYPE_MAIN_VARIANT (TREE_TYPE (expr));
269 if (want_pointer)
270 probe = TYPE_MAIN_VARIANT (TREE_TYPE (probe));
271
272 my_friendly_assert (code == MINUS_EXPR
273 ? same_type_p (BINFO_TYPE (binfo), probe)
274 : code == PLUS_EXPR
275 ? same_type_p (BINFO_TYPE (d_binfo), probe)
276 : false, 20010723);
277
278 if (code == MINUS_EXPR && v_binfo)
279 {
280 error ("cannot convert from base `%T' to derived type `%T' via virtual base `%T'",
281 BINFO_TYPE (binfo), BINFO_TYPE (d_binfo), BINFO_TYPE (v_binfo));
282 return error_mark_node;
283 }
284
285 if (!want_pointer)
286 /* This must happen before the call to save_expr. */
287 expr = build_unary_op (ADDR_EXPR, expr, 0);
288
289 fixed_type_p = resolves_to_fixed_type_p (expr, &nonnull);
290 if (fixed_type_p <= 0 && TREE_SIDE_EFFECTS (expr))
291 expr = save_expr (expr);
Before invoking below function, if want_pointer is zero which means expr is not pointer. It wraps expr into an ADDR_EXPR node which will force nonnull into nonzero with below function. Note that t at line 5410 is the static type of instance , but it may not be the same as the real type (dynamic type). Considering below example, it will give the same result as example 3:
class A { public : example - 5
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 { public : virtual B2* f () { printf ("B2::f/n"); return 0; } };
class C: public B1, public B2 { };
int main() {
A *pa = new C;
A* (A::*pfn) () = &A::f;
(pa->*pfn)();
delete pa;
return 0;
}
Then for pa , its static type is “A*” as it is declared, and its dynamic type is “C*” which is determined at runtime with the new statement. And note pfn finally calls B2::f due to the virtual function mechanism.
Function resolves_to_fixed_type_p returns nonzero if the static type equates to the dynamic type (and if instance is ctor/dtor too, negative value is returned instead).
5407 int
5408 resolves_to_fixed_type_p (tree instance, int* nonnull) in class.c
5409 {
5410 tree t = TREE_TYPE (instance);
5411 int cdtorp = 0;
5412
5413 tree fixed = fixed_type_or_null (instance, nonnull, &cdtorp);
5414 if (fixed == NULL_TREE)
5415 return 0;
5416 if (POINTER_TYPE_P (t))
5417 t = TREE_TYPE (t);
5418 if (!same_type_ignoring_top_level_qualifiers_p (t, fixed))
5419 return 0;
5420 return cdtorp ? -1 : 1;
5421 }
The dynamic type of instance , if known is returned by fixed_type_or_null , in which argument nonnull is set if and only if instance can be known to be nonnull, regardless of our knowledge of its type; and argument cdtorp is nonzero if instance is also the expression of ‘this’ pointer.