GCC-3.4.6源代码学习笔记(162-续)
2011-02-19 11:36
330 查看
在
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
指向所使用的函数的类型。然后需要根据参数声明,为实参执行转换。
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
指向所使用的函数的类型。然后需要根据参数声明,为实参执行转换。
相关文章推荐
- GCC-3.4.6源代码学习笔记(162)
- GCC-3.4.6源代码学习笔记(54)
- GCC-3.4.6源代码学习笔记(90)
- GCC-3.4.6源代码学习笔记(45)
- GCC-3.4.6源代码学习笔记(27)
- GCC-3.4.6源代码学习笔记(10)
- GCC-3.4.6源代码学习笔记(92)
- GCC-3.4.6源代码学习笔记(30)
- GCC-3.4.6源代码学习笔记(13)
- GCC-3.4.6源代码学习笔记(97)
- GCC-3.4.6源代码学习笔记(142-续1)
- GCC-3.4.6源代码学习笔记(98)
- GCC-3.4.6源代码学习笔记(36)
- GCC-3.4.6源代码学习笔记(16)
- GCC-3.4.6源代码学习笔记(147-续1)
- GCC-3.4.6源代码学习笔记(48续)
- GCC-3.4.6源代码学习笔记(122)
- GCC-3.4.6源代码学习笔记(126)
- GCC-3.4.6源代码学习笔记(52)
- GCC-3.4.6源代码学习笔记(135)