Studying note of GCC-3.4.6 source (162)

5.13.4.7.1.        Generating code for function invocation
5.13.4.7.1.1.  Build expression to take function’s address

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.

你可能感兴趣的:(function,tree,Integer,Class,Build,fold)