GCC-3.4.6源代码学习笔记(152)
2011-01-29 15:06
405 查看
5.12.5.2.2.2.2.
缺省实参
如果耗尽了
arg
,但
parm
还没有,
parm
必然包含了缺省实参,该形参列表应该以特殊节点
void_list_node
来结尾。那么在对应的节点中,
TREE_VALUE
保存了该类型,而
TREE_PURPOSE
是缺省实参的表达式。
4258
tree
4259
convert_default_arg
(tree type, tree arg, tree fn, int parmnum)
in call.c
4260
{
4261
/*
If the ARG is an unparsed default argument expression, the
4262
conversion cannot be performed.
*/
4263
if (TREE_CODE (arg) ==
DEFAULT_ARG)
4264
{
4265
error ("the default
argument for parameter %d of `%D' has "
4266
"not yet been
parsed",
4267
parmnum, fn);
4268
return
error_mark_node;
4269
}
4270
4271
if (fn &&
DECL_TEMPLATE_INFO (fn))
4272
arg =
tsubst_default_argument (fn, type, arg);
4273
4274
arg = break_out_target_exprs
(arg);
4275
4276
if (TREE_CODE (arg) ==
CONSTRUCTOR)
4277
{
4278
arg = digest_init
(type, arg, 0);
4279
arg =
convert_for_initialization (0, type, arg, LOOKUP_NORMAL,
4280
"default
argument", fn, parmnum);
4281
}
4282
else
4283
{
4284
/*
This could get clobbered by the following call.
*/
4285
if (TREE_HAS_CONSTRUCTOR
(arg))
4286
arg = copy_node (arg);
4287
4288
arg =
convert_for_initialization (0, type, arg, LOOKUP_NORMAL,
4289
"default
argument", fn, parmnum);
4290
arg = convert_for_arg_passing
(type, arg);
4291
}
4292
4293
return
arg;
4294
}
节点
DEFAULT_ARG
是为未解析的缺省实参所构建的。记得在解析类定义的过程中,缺省实参被
DEFAULT_ARG
所缓存,它将在该解析结束后才解析。因此在这里不应该出现
DEFAULT_ARG
(如果是这样,这可能是在类定义中缺少了“
};
”),而且在这一点上,前端不知道如何处理这种节点。
那么在
4274
传递给下面函数的
arg
,是由该函数所有调用所共享的缺省实参。不过,正如【
3
】所定义的,“每次调用该函数都要评估缺省实参
”,因此在真正评估这个缺省实参之前,需要
准备合适的
arg
的拷贝(我们不能直接改变
arg
),并如下所示地更新这个局部临时对象。
1259
tree
1260
break_out_target_exprs
(tree t)
in
cp/tree.c
1261
{
1262
static
int target_remap_count;
1263
static
splay_tree target_remap;
1264
1265
if (!target_remap_count++)
1266
target_remap =
splay_tree_new (splay_tree_compare_pointers,
1267
/*splay_tree_delete_key_fn=*/
NULL,
1268
/*splay_tree_delete_value_fn=*/
NULL);
1269
walk_tree
(&t, bot_manip
, target_remap, NULL);
1270
walk_tree
(&t, bot_replace
, target_remap, NULL);
1271
1272
if (!--target_remap_count)
1273
{
1274
splay_tree_delete
(target_remap);
1275
target_remap = NULL;
1276
}
1277
1278
return
t;
1279
}
上面,
walk_tree
遍历以
t
为根的子树,并在以
t
的编码所选出的节点上执行给定的函数。在第一次遍历中,使用下面的函数。注意该函数总是返回
NULL
(下面的
copy_tree_r
返回
NULL
)来强制
walk_tree
执行深度优先的完整遍历(即,
tp
可能是一个
tree_list
,其中的节点可以包含操作数,它们依次亦可能是
tree_list
,等等,访问将从底部开始向上);不过是否进入子树(即操作数)由局部变量
walk_subtrees
来控制(它在下面的函数中作为实参
walk_subtrees
传入)。在使用指定的函数处理树节点前,
walk_subtrees
被设置为
1
;是指定的函数来决定该节点是否是感兴趣的,并且需要进入。
1182
static
tree
1183
bot_manip
(tree* tp, int* walk_subtrees, void* data)
in cp/tree.c
1184
{
1185
splay_tree target_remap =
((splay_tree) data);
1186
tree t = *tp;
1187
1188
if (TREE_CONSTANT (t))
1189
{
1190
/*
There can't be any TARGET_EXPRs or their slot variables below
1191
this
point. We used to check !TREE_SIDE_EFFECTS, but then we
1192
failed to copy an ADDR_EXPR of the slot
VAR_DECL.
*/
1193
*walk_subtrees = 0;
1194
return
NULL_TREE;
1195
}
1196
if (TREE_CODE (t) ==
TARGET_EXPR)
1197
{
1198
tree u;
1199
1200
if (TREE_CODE
(TREE_OPERAND (t, 1)) == AGGR_INIT_EXPR)
1201
{
1202
mark_used
(TREE_OPERAND (TREE_OPERAND (TREE_OPERAND (t, 1), 0), 0));
1203
u = build_cplus_new
1204
(TREE_TYPE (t),
break_out_target_exprs (TREE_OPERAND (t, 1)));
1205
}
1206
else
1207
{
1208
u = build_target_expr_with_type
1209
(break_out_target_exprs
(TREE_OPERAND (t,
1)), TREE_TYPE (t));
1210
}
1211
1212
/* Map the old variable to the new one.
*/
1213
splay_tree_insert
(target_remap,
1214
(splay_tree_key)
TREE_OPERAND (t, 0),
1215
(splay_tree_value)
TREE_OPERAND (u, 0));
1216
1217
/*
Replace the old expression with the new version.
*/
1218
*tp = u;
1219
/*
We don't have to go below this point; the recursive call to
1220
break_out_target_exprs will have handled
anything below this
1221
point.
*/
1222
*walk_subtrees = 0;
1223
return
NULL_TREE;
1224
}
1225
else if (TREE_CODE (t) ==
CALL_EXPR)
1226
mark_used
(TREE_OPERAND (TREE_OPERAND (t, 0), 0));
1227
1228
/*
Make a copy of this node.
*/
1229
return
copy_tree_r
(tp, walk_subtrees, NULL);
1230
}
对于常量或
TRAGET_EXPR
以外的节点,
copy_tree_r
拷贝这个节点,如果它是
*_CST
(看到它其实被上面的
TREE_CONSTANT
滤掉了),或表达式,或
TREE_LIST
,或
TREE_VEC
,或
OVERLOAD
(由下面的
C++
的钩子
tree_chain_matters_p
来辨别,并注意到在这里
walk_subtrees
没有改变,
walk_tree
将进入该节点的子节点,并继续拷贝其结构)。
1966
tree
1967
copy_tree_r
(tree *tp, int
*walk_subtrees, void *data ATTRIBUTE_UNUSED)
in tree-inline.c
1968
{
1969
enum
tree_code code = TREE_CODE (*tp);
1970
1971
/* We make copies of most nodes.
*/
1972
if (IS_EXPR_CODE_CLASS
(TREE_CODE_CLASS (code))
1973
|| TREE_CODE_CLASS
(code) == 'c'
1974
|| code == TREE_LIST
1975
|| code == TREE_VEC
1976
|| (*lang_hooks
.tree_inlining.tree_chain_matters_p)
(*tp))
1977
{
1978
/* Because the chain gets clobbered when we
make a copy, we save it
1979
here.
*/
1980
tree chain = TREE_CHAIN
(*tp);
1981
1982
/*
Copy the node.
*/
1983
*tp = copy_node (*tp);
1984
1985
/*
Now, restore the chain, if appropriate. That will cause
1986
walk_tree to walk into the chain as
well.
*/
1987
if (code == PARM_DECL ||
code == TREE_LIST
1988
#ifndef
INLINER_FOR_JAVA
1989
|| (*lang_hooks
.tree_inlining.tree_chain_matters_p)
(*tp)
1990
|| STATEMENT_CODE_P (code))
1991
TREE_CHAIN (*tp) = chain;
1992
1993
/* For now, we don't update BLOCKs when we
make copies. So, we
1994
have
to nullify all scope-statements.
*/
1995
if (TREE_CODE (*tp) ==
SCOPE_STMT)
1996
SCOPE_STMT_BLOCK (*tp) =
NULL_TREE;
1997
#else
/* INLINER_FOR_JAVA */
1998
|| (*lang_hooks
.tree_inlining.tree_chain_matters_p)
(*tp))
1999
TREE_CHAIN (*tp) =
chain;
2000
#endif
/*
INLINER_FOR_JAVA */
2001
}
2002
else if (TREE_CODE_CLASS
(code) == 't')
2003
*walk_subtrees = 0;
2004
2005
return
NULL_TREE;
2006
}
一个
TARGET_EXPR
代表一个临时对象。其第一个操作数是表示这个临时对象的一个
VAR_DECL
。其第二个操作数是该临时对象的初始值。该初始值被评估,然后拷贝入这个临时对象。
一个
AGGR_INIT_EXPR
代表,作为一个函数调用返回值,或一个构造函数结果的初始化。一个
AGGR_INIT_EXPR
将仅出现作为一个
TARGET_EXPR
的第二个操作数。该
AGGR_INIT_EXPR
的第一个操作数是所调用函数的地址,就像在一个
CALL_EXPR
那样。第二个操作数是以一个
TREE_LIST
形式传递给该函数的实参,同样类似于在一个
CALL_EXPR
里那样。这个表达式的值由这个函数返回。
那么如果
AGGR_INIT_EXPR
被用于
TRAGET_EXPR
里,递归
break_out_target_exprs
来拷贝这个节点。对于这个拷贝过来的表达式,
build_cplus_new
为其初始化产生了代码。
2007
tree
2008
build_cplus_new
(tree type, tree init)
in
cp/tree.c
2009
{
2010
tree fn;
2011
tree slot;
2012
tree rval;
2013
int is_ctor;
2014
2015
/*
Make sure that we're not trying to create an instance of an
2016
abstract class.
*/
2017
abstract_virtuals_error
(NULL_TREE, type);
2018
2019
if (TREE_CODE (init) != CALL_EXPR &&
TREE_CODE (init) != AGGR_INIT_EXPR)
2020
return
convert (type, init);
2021
2022
fn = TREE_OPERAND (init, 0);
2023
is_ctor = (TREE_CODE (fn) ==
ADDR_EXPR
2024
&&
TREE_CODE (TREE_OPERAND (fn, 0)) == FUNCTION_DECL
2025
&& DECL_CONSTRUCTOR_P
(TREE_OPERAND (fn, 0)));
2026
2027
slot = build_local_temp
(type);
2028
2029
/*
We split the CALL_EXPR into its function and its arguments here.
2030
Then,
in expand_expr, we put them back together. The reason for
2031
this
is that this expression might be a default argument
2032
expression. In that case, we need a new
temporary every time the
2033
expression is used. That's what
break_out_target_exprs does; it
2034
replaces every AGGR_INIT_EXPR with a copy
that uses a fresh
2035
temporary slot. Then, expand_expr builds up
a call-expression
2036
using
the new slot.
*/
2037
2038
/*
If we don't need to use a constructor to create an object of this
2039
type,
don't mess with AGGR_INIT_EXPR.
*/
2040
if (is_ctor ||
TREE_ADDRESSABLE (type))
2041
{
2042
rval = build
(AGGR_INIT_EXPR, type, fn, TREE_OPERAND (init, 1), slot);
2043
TREE_SIDE_EFFECTS (rval) =
1;
2044
AGGR_INIT_VIA_CTOR_P
(rval) = is_ctor;
2045
}
2046
else
2047
rval = init;
2048
2049
rval = build_target_expr
(slot, rval);
2050
2051
return
rval;
2052
}
现在需要更新在
TARGET_EXPR
中的临时对象,因为我们不是在产生原始的
TARGET_EXPR
的上下文中。看到这个临时对象成为了局部的,其
DECL_CONTEXT
被强制设置为
current_function_decl
。
253
static
tree
254
build_local_temp
(tree type)
in
cp/tree.c
255
{
256
tree slot = build_decl
(VAR_DECL, NULL_TREE, type);
257
DECL_ARTIFICIAL (slot) = 1;
258
DECL_CONTEXT (slot) = current_function_decl
;
259
layout_decl
(slot,
0);
260
return
slot;
261
}
注意如果对于这个
AGGR_INIT_EXPR
,
AGGR_INIT_VIA_CTOR_P
成立,表示这个初始化是通过一个构造函数调用实现。在
2042
行所构建的
AGGR_INIT_EXPR
,其第三个操作数是这个临时对象,它总是一个
VAR_DECL
。而
init
是原来
TARGET_EXPR
中的
AGGR_INIT_EXPR
(这个
AGGR_INIT_EXPR
的第三个操作数的地址被提取,并替换实参列表中的值。这个情况下,该表达式的值是提供给这个
AGGR_INIT_EXPR
的
VAR_DECL
;构造函数不返回值)。
最后,这个新构建的
TARGET_EXPR
被
build_cplus_new
返回。
而对于
TARGET_EXPR
的第二个操作数不是
AGGR_INIT_EXPR
的情况,该操作数由
build_target_expr_with_type
来处理。这里如果
init
是
TARGET_EXPR
,它必定是由在
bot_manip
中
1209
行的
break_out_target_exprs
所构建的,这个节点正是我们期望的。
320
tree
321
build_target_expr_with_type
(tree init,
tree type)
in
cp/tree.c
322
{
323
tree slot;
324
325
if (TREE_CODE (init) == TARGET_EXPR)
326
return
init;
327
else if (CLASS_TYPE_P (type) &&
!TYPE_HAS_TRIVIAL_INIT_REF (type)
328
&& TREE_CODE (init) !=
COND_EXPR
329
&& TREE_CODE (init) !=
CONSTRUCTOR
330
&& TREE_CODE (init) !=
VA_ARG_EXPR)
331
/* We need to
build up a copy constructor call. COND_EXPR is a special
332
case because we
already have copies on the arms and we don't want
333
another one
here. A CONSTRUCTOR is aggregate initialization, which
334
is handled
separately. A VA_ARG_EXPR is magic creation of an
335
aggregate; there's no additional work to be
done.
*/
336
return
force_rvalue
(init);
337
338
slot = build_local_temp
(type);
339
return
build_target_expr
(slot, init);
340
}
上面,如果
TYPE_HAS_TRIVIAL_INIT_REF
不是
0
,表示该拷贝初始化可以使用按位拷贝。对于这样的情形,可以简单地构建
TARGET_EXPR
;否则,需要执行一个左值到右值的转换,包括如下所示的拷贝构造函数调用。
590
tree
591
force_rvalue
(tree expr)
in
cvt.c
592
{
593
if (IS_AGGR_TYPE (TREE_TYPE
(expr)) && TREE_CODE (expr) != TARGET_EXPR)
594
expr = ocp_convert (TREE_TYPE (expr), expr,
595
CONV_IMPLICIT|CONV_FORCE_TEMP, LOOKUP_NORMAL);
596
else
597
expr = decay_conversion
(expr);
598
599
return
expr;
600
}
不久之后我们将看到
ocp_convert
的细节。在这里总而言之,该函数将产生调用合适的拷贝构造函数代码,然后调用
build_cplus_new
来产生构建这个临时对象及其初始化。在离开
bot_manip
之前,看一下
build_target_expr
。
234
static
tree
235
build_target_expr
(tree decl, tree
value)
in
cp/tree.c
236
{
237
tree t;
238
239
t = build
(TARGET_EXPR,
TREE_TYPE (decl), decl, value,
240
cxx_maybe_build_cleanup
(decl), NULL_TREE);
241
/* We always set
TREE_SIDE_EFFECTS so that expand_expr does not
242
ignore the
TARGET_EXPR. If there really turn out to be no
243
side-effects,
then the optimizer should be able to get rid of
244
whatever code is
generated anyhow.
*/
245
TREE_SIDE_EFFECTS (t) = 1;
246
247
return
t;
248
}
对于具有非平凡析构函数的临时对象,编译器需要产生代码,在其越出其作用域时,通过调用这个析构函数摧毁这个临时对象。因此在
240
行,
cxx_maybe_build_cleanup
产生这些代码,如果需要的话。
现在在
bot_manip
的
1213
行,
u
是对应于
t
的
TARGET_EXPR
。它把新旧版本的临时对象映射起来。然后,我们立即用新版本取代了旧版本。不过,在由
break_out_target_exprs
处理的节点的某些子节点中,可能仍然保留了这个旧版本的引用,它们需要如下的更新。
1236
static
tree
1237
bot_replace
(tree* t,
in
cp/tree.c
1238
int* walk_subtrees
ATTRIBUTE_UNUSED ,
1239
void* data)
1240
{
1241
splay_tree target_remap =
((splay_tree) data);
1242
1243
if (TREE_CODE (*t) ==
VAR_DECL)
1244
{
1245
splay_tree_node n =
splay_tree_lookup (target_remap,
1246
(splay_tree_key)
*t);
1247
if (n)
1248
*t = (tree) n->value;
1249
}
1250
1251
return
NULL_TREE;
1252
}
回到
convert_default_arg
,在
4274
行从
break_out_target_exprs
得到这个更新的
arg
,然后接下来的函数用于产生初始化代码。
5.12.5.2.2.2.3.
省略实参
最后一个的可能就是省略实参,注意到省略实参与缺省实参不可共存。在前端中,为了识别包含了省略实参的函数声明,形参列表由
NULL
,而不是
void_list_node
来结尾。
4161
tree
4162
convert_arg_to_ellipsis
(tree arg)
in
call.c
4163
{
4164
/*
[expr.call]
4165
4166
The
lvalue-to-rvalue, array-to-pointer, and function-to-pointer
4167
standard conversions are performed.
*/
4168
arg = decay_conversion
(arg);
4169
/* [expr.call]
4170
4171
If the
argument has integral or enumeration type that is subject
4172
to the
integral promotions (_conv.prom_), or a floating point
4173
type
that is subject to the floating point promotion
4174
(_conv.fpprom_), the value of the argument
is converted to the
4175
promoted type before the call.
*/
4176
if (TREE_CODE (TREE_TYPE
(arg)) == REAL_TYPE
4177
&&
(TYPE_PRECISION (TREE_TYPE (arg))
4178
< TYPE_PRECISION
(double_type_node)))
4179
arg = convert_to_real
(double_type_node, arg);
4180
else if (INTEGRAL_OR_ENUMERATION_TYPE_P
(TREE_TYPE (arg)))
4181
arg =
perform_integral_promotions (arg);
4182
4183
arg = require_complete_type
(arg);
4184
4185
if (arg != error_mark_node
4186
&& !pod_type_p
(TREE_TYPE (arg)))
4187
{
4188
/*
Undefined behavior [expr.call] 5.2.2/7. We used
to just warn
4189
here
and do a bitwise copy, but now cp_expr_size will abort if we
4190
try
to do that.
4191
If
the call appears in the context of a sizeof expression,
4192
there is no need to emit a warning, since
the expression won't be
4193
evaluated. We keep the builtin_trap just
as a safety check.
*/
4194
if (!skip_evaluation
)
4195
warning ("cannot
pass objects of non-POD type `%#T' through `...'; "
4196
"call will
abort at runtime", TREE_TYPE (arg));
4197
arg = call_builtin_trap
();
4198
arg = build
(COMPOUND_EXPR, integer_type_node, arg,
4199
integer_zero_node);
4200
}
4201
4202
return
arg;
4203
}
【
3
】的条文
5.2.2
“函数调用”,条款
7
如下定义了编译器在省略实参上的行为。
函数
decay_conversion
执行在
exp
中的转换,它应用在当一个左值出现在一个右值上下文中时候。包括左值到右值,数组到指针,及函数到指针的转换。
1335
tree
1336
decay_conversion
(tree exp)
n
typeck.c
1337
{
1338
tree type;
1339
enum
tree_code code;
1340
1341
type = TREE_TYPE (exp);
1342
code = TREE_CODE (type);
1343
1344
if (code == REFERENCE_TYPE)
1345
{
1346
exp = convert_from_reference
(exp);
1347
type = TREE_TYPE (exp);
1348
code = TREE_CODE (type);
1349
}
1350
1351
if (type == error_mark_node)
1352
return
error_mark_node;
1353
1354
if (type_unknown_p (exp))
1355
{
1356
cxx_incomplete_type_error
(exp, TREE_TYPE (exp));
1357
return
error_mark_node;
1358
}
1359
1360
/*
Constants can be used directly unless they're not loadable.
*/
1361
if (TREE_CODE (exp) ==
CONST_DECL)
1362
exp = DECL_INITIAL (exp);
1363
/*
Replace a nonvolatile const static variable with its value. We
1364
don't
do this for arrays, though; we want the address of the
1365
first
element of the array, not the address of the first element
1366
of its
initializing constant.
*/
1367
else if (code != ARRAY_TYPE)
1368
{
1369
exp = decl_constant_value
(exp);
1370
type = TREE_TYPE (exp);
1371
}
1372
1373
/* build_c_cast puts on a NOP_EXPR to make the
result not an lvalue.
1374
Leave
such NOP_EXPRs, since RHS is being used in non-lvalue context.
*/
1375
1376
if (code == VOID_TYPE)
1377
{
1378
error ("void value
not ignored as it ought to be");
1379
return
error_mark_node;
1380
}
1381
if
(invalid_nonstatic_memfn_p (exp))
1382
return
error_mark_node;
1383
if (code == FUNCTION_TYPE ||
is_overloaded_fn
(exp))
1384
return
build_unary_op (ADDR_EXPR, exp, 0);
1385
if (code == ARRAY_TYPE)
1386
{
1387
tree adr;
1388
tree ptrtype;
1389
1390
if (TREE_CODE (exp) ==
INDIRECT_REF)
1391
return
build_nop (build_pointer_type
(TREE_TYPE
(type)),
1392
TREE_OPERAND (exp, 0));
1393
1394
if (TREE_CODE (exp) ==
COMPOUND_EXPR)
1395
{
1396
tree op1 = decay_conversion
(TREE_OPERAND (exp, 1));
1397
return
build
(COMPOUND_EXPR, TREE_TYPE (op1),
1398
TREE_OPERAND
(exp, 0), op1);
1399
}
1400
1401
if (!lvalue_p (exp)
1402
&& ! (TREE_CODE
(exp) == CONSTRUCTOR && TREE_STATIC (exp)))
1403
{
1404
error ("invalid use
of non-lvalue array");
1405
return
error_mark_node;
1406
}
1407
1408
ptrtype = build_pointer_type
(TREE_TYPE (type));
1409
1410
if (TREE_CODE (exp) ==
VAR_DECL)
1411
{
1412
if
(!cxx_mark_addressable (exp))
1413
return
error_mark_node;
1414
adr = build_nop
(ptrtype, build_address
(exp));
1415
TREE_SIDE_EFFECTS (adr)
= 0;
/*
Default would be, same as EXP.
*/
1416
return
adr;
1417
}
1418
/*
This way is better for a COMPONENT_REF since it can
1419
simplify the offset for a component.
*/
1420
adr = build_unary_op
(ADDR_EXPR, exp, 1);
1421
return
cp_convert (ptrtype, adr);
1422
}
1423
1424
/*
[basic.lval]: Class rvalues can have cv-qualified types; non-class
1425
rvalues always have cv-unqualified
types.
*/
1426
if (! CLASS_TYPE_P (type))
1427
exp = cp_convert
(TYPE_MAIN_VARIANT (type), exp);
1428
1429
return
exp;
1430
}
首先对于
REFERENCE_TYPE
,它是一个左值,要把它转换为右值,就应该使用被引用的值,而不再保存引用。在前端中,
INDIRECT_REF
为这个目标而构建(这是因为引用总是传入其地址就像指针那样;而在编译器中,引用的模式(
mode
)与指针的相同,都是
ptr_mode
,参见
build_reference_type
)。
566
tree
567
convert_from_reference
(tree val)
in cvt.c
568
{
569
if (TREE_CODE (TREE_TYPE (val)) ==
REFERENCE_TYPE)
570
return
build_indirect_ref
(val, NULL);
571
return
val;
572
}
记得指针是右值。当处理
ARRAY_TYPE
时,如果我们正在使用形如:
int *a[i]
(
a
是维度大于
1
的数组,例如,
int
A[2][2]
)的表达式,满足
1390
行的条件,
因此构建了形如:
int**
的类型,并且看到转换指针“
int
*a[i]
”到“
int**
”不需要产生任何代码,因而为这个转换构建了
NOP_EXPR
。
而如果
exp
只是一个数组的声明,例如:
int a[8]
,这个声明的右值是“
int*
”。可以直接构建这个右值。不过,对于其他的情形,例如:“
tempA.a
”(这是一棵以
SCOPE_REF
为根节点的树),就没有可以直接使用的简单规则,因此调用
build_unary_op
及
cp_convert
来执行合适的转换。
正如在
convert_arg_to_ellipsis
的
4188
行的注释所提到的,对于对应于
non-POD
类类型的未定义行为,
GCC
以前使用按位拷贝,不过在当前版本中,这个行为在后面对
cp_expr_size
的调用将导致异常终止。这个终止由下面“
__builtin_trap
”的调用触发,在
4198
行它成为了
COMPOUND_EXPR
形式的
arg
的一部分。
4148
c
tree
4149
call_builtin_trap
(void)
in
call.c
4150
{
4151
tree fn = IDENTIFIER_GLOBAL_VALUE
(get_identifier ("__builtin_trap"));
4152
4153
my_friendly_assert (fn !=
NULL, 20030927);
4154
fn = build_call (fn,
NULL_TREE);
4155
return
fn;
4156
}
内建陷阱的行为是:如果目标机器定义了陷阱指令,就使用它;否则编译器将调用
abort
()
(参考
expand_builtin_trap
)。
下面是对
non-POD
类类型省略实参的一个有趣的测试:
#include
<stdarg.h>
class
A {
public
: virtual
void func() {}
};
int func (int
a, ...) {
va_list ap;
va_start(ap, a)
;
va_arg(ap, A);
va_end(ap);
return
1;
}
int main() {
A a;
func (1, a);
// sizeof (func (1, a))
}
编译器将给出以下警告:
test2.cpp: In function ‘int func(int, ...)’:
test2.cpp:11: warning: cannot receive objects of
non-POD type ‘class A’ through ‘...’; call will abort at runtime
test2.cpp: In function ‘int main()’:
test2.cpp:19: warning: cannot pass objects of non-POD
type ‘class A’ through ’...’; call will abort at runtime
当执行这个程序时,得到错误:
Illegal instruction.
不过,如果我们使用注释中的语句,编译器将给出警告:
test2.cpp: In function ‘int func(int, ...)’:
test2.cpp:11: warning: cannot receive objects of
non-POD type ‘class A’ through ‘...’; call will abort at runtime
而在执行时,没有产生错误,因为
func(1, a)
没有被评估,除了其返回值。
缺省实参
如果耗尽了
arg
,但
parm
还没有,
parm
必然包含了缺省实参,该形参列表应该以特殊节点
void_list_node
来结尾。那么在对应的节点中,
TREE_VALUE
保存了该类型,而
TREE_PURPOSE
是缺省实参的表达式。
4258
tree
4259
convert_default_arg
(tree type, tree arg, tree fn, int parmnum)
in call.c
4260
{
4261
/*
If the ARG is an unparsed default argument expression, the
4262
conversion cannot be performed.
*/
4263
if (TREE_CODE (arg) ==
DEFAULT_ARG)
4264
{
4265
error ("the default
argument for parameter %d of `%D' has "
4266
"not yet been
parsed",
4267
parmnum, fn);
4268
return
error_mark_node;
4269
}
4270
4271
if (fn &&
DECL_TEMPLATE_INFO (fn))
4272
arg =
tsubst_default_argument (fn, type, arg);
4273
4274
arg = break_out_target_exprs
(arg);
4275
4276
if (TREE_CODE (arg) ==
CONSTRUCTOR)
4277
{
4278
arg = digest_init
(type, arg, 0);
4279
arg =
convert_for_initialization (0, type, arg, LOOKUP_NORMAL,
4280
"default
argument", fn, parmnum);
4281
}
4282
else
4283
{
4284
/*
This could get clobbered by the following call.
*/
4285
if (TREE_HAS_CONSTRUCTOR
(arg))
4286
arg = copy_node (arg);
4287
4288
arg =
convert_for_initialization (0, type, arg, LOOKUP_NORMAL,
4289
"default
argument", fn, parmnum);
4290
arg = convert_for_arg_passing
(type, arg);
4291
}
4292
4293
return
arg;
4294
}
节点
DEFAULT_ARG
是为未解析的缺省实参所构建的。记得在解析类定义的过程中,缺省实参被
DEFAULT_ARG
所缓存,它将在该解析结束后才解析。因此在这里不应该出现
DEFAULT_ARG
(如果是这样,这可能是在类定义中缺少了“
};
”),而且在这一点上,前端不知道如何处理这种节点。
那么在
4274
传递给下面函数的
arg
,是由该函数所有调用所共享的缺省实参。不过,正如【
3
】所定义的,“每次调用该函数都要评估缺省实参
”,因此在真正评估这个缺省实参之前,需要
准备合适的
arg
的拷贝(我们不能直接改变
arg
),并如下所示地更新这个局部临时对象。
1259
tree
1260
break_out_target_exprs
(tree t)
in
cp/tree.c
1261
{
1262
static
int target_remap_count;
1263
static
splay_tree target_remap;
1264
1265
if (!target_remap_count++)
1266
target_remap =
splay_tree_new (splay_tree_compare_pointers,
1267
/*splay_tree_delete_key_fn=*/
NULL,
1268
/*splay_tree_delete_value_fn=*/
NULL);
1269
walk_tree
(&t, bot_manip
, target_remap, NULL);
1270
walk_tree
(&t, bot_replace
, target_remap, NULL);
1271
1272
if (!--target_remap_count)
1273
{
1274
splay_tree_delete
(target_remap);
1275
target_remap = NULL;
1276
}
1277
1278
return
t;
1279
}
上面,
walk_tree
遍历以
t
为根的子树,并在以
t
的编码所选出的节点上执行给定的函数。在第一次遍历中,使用下面的函数。注意该函数总是返回
NULL
(下面的
copy_tree_r
返回
NULL
)来强制
walk_tree
执行深度优先的完整遍历(即,
tp
可能是一个
tree_list
,其中的节点可以包含操作数,它们依次亦可能是
tree_list
,等等,访问将从底部开始向上);不过是否进入子树(即操作数)由局部变量
walk_subtrees
来控制(它在下面的函数中作为实参
walk_subtrees
传入)。在使用指定的函数处理树节点前,
walk_subtrees
被设置为
1
;是指定的函数来决定该节点是否是感兴趣的,并且需要进入。
1182
static
tree
1183
bot_manip
(tree* tp, int* walk_subtrees, void* data)
in cp/tree.c
1184
{
1185
splay_tree target_remap =
((splay_tree) data);
1186
tree t = *tp;
1187
1188
if (TREE_CONSTANT (t))
1189
{
1190
/*
There can't be any TARGET_EXPRs or their slot variables below
1191
this
point. We used to check !TREE_SIDE_EFFECTS, but then we
1192
failed to copy an ADDR_EXPR of the slot
VAR_DECL.
*/
1193
*walk_subtrees = 0;
1194
return
NULL_TREE;
1195
}
1196
if (TREE_CODE (t) ==
TARGET_EXPR)
1197
{
1198
tree u;
1199
1200
if (TREE_CODE
(TREE_OPERAND (t, 1)) == AGGR_INIT_EXPR)
1201
{
1202
mark_used
(TREE_OPERAND (TREE_OPERAND (TREE_OPERAND (t, 1), 0), 0));
1203
u = build_cplus_new
1204
(TREE_TYPE (t),
break_out_target_exprs (TREE_OPERAND (t, 1)));
1205
}
1206
else
1207
{
1208
u = build_target_expr_with_type
1209
(break_out_target_exprs
(TREE_OPERAND (t,
1)), TREE_TYPE (t));
1210
}
1211
1212
/* Map the old variable to the new one.
*/
1213
splay_tree_insert
(target_remap,
1214
(splay_tree_key)
TREE_OPERAND (t, 0),
1215
(splay_tree_value)
TREE_OPERAND (u, 0));
1216
1217
/*
Replace the old expression with the new version.
*/
1218
*tp = u;
1219
/*
We don't have to go below this point; the recursive call to
1220
break_out_target_exprs will have handled
anything below this
1221
point.
*/
1222
*walk_subtrees = 0;
1223
return
NULL_TREE;
1224
}
1225
else if (TREE_CODE (t) ==
CALL_EXPR)
1226
mark_used
(TREE_OPERAND (TREE_OPERAND (t, 0), 0));
1227
1228
/*
Make a copy of this node.
*/
1229
return
copy_tree_r
(tp, walk_subtrees, NULL);
1230
}
对于常量或
TRAGET_EXPR
以外的节点,
copy_tree_r
拷贝这个节点,如果它是
*_CST
(看到它其实被上面的
TREE_CONSTANT
滤掉了),或表达式,或
TREE_LIST
,或
TREE_VEC
,或
OVERLOAD
(由下面的
C++
的钩子
tree_chain_matters_p
来辨别,并注意到在这里
walk_subtrees
没有改变,
walk_tree
将进入该节点的子节点,并继续拷贝其结构)。
1966
tree
1967
copy_tree_r
(tree *tp, int
*walk_subtrees, void *data ATTRIBUTE_UNUSED)
in tree-inline.c
1968
{
1969
enum
tree_code code = TREE_CODE (*tp);
1970
1971
/* We make copies of most nodes.
*/
1972
if (IS_EXPR_CODE_CLASS
(TREE_CODE_CLASS (code))
1973
|| TREE_CODE_CLASS
(code) == 'c'
1974
|| code == TREE_LIST
1975
|| code == TREE_VEC
1976
|| (*lang_hooks
.tree_inlining.tree_chain_matters_p)
(*tp))
1977
{
1978
/* Because the chain gets clobbered when we
make a copy, we save it
1979
here.
*/
1980
tree chain = TREE_CHAIN
(*tp);
1981
1982
/*
Copy the node.
*/
1983
*tp = copy_node (*tp);
1984
1985
/*
Now, restore the chain, if appropriate. That will cause
1986
walk_tree to walk into the chain as
well.
*/
1987
if (code == PARM_DECL ||
code == TREE_LIST
1988
#ifndef
INLINER_FOR_JAVA
1989
|| (*lang_hooks
.tree_inlining.tree_chain_matters_p)
(*tp)
1990
|| STATEMENT_CODE_P (code))
1991
TREE_CHAIN (*tp) = chain;
1992
1993
/* For now, we don't update BLOCKs when we
make copies. So, we
1994
have
to nullify all scope-statements.
*/
1995
if (TREE_CODE (*tp) ==
SCOPE_STMT)
1996
SCOPE_STMT_BLOCK (*tp) =
NULL_TREE;
1997
#else
/* INLINER_FOR_JAVA */
1998
|| (*lang_hooks
.tree_inlining.tree_chain_matters_p)
(*tp))
1999
TREE_CHAIN (*tp) =
chain;
2000
#endif
/*
INLINER_FOR_JAVA */
2001
}
2002
else if (TREE_CODE_CLASS
(code) == 't')
2003
*walk_subtrees = 0;
2004
2005
return
NULL_TREE;
2006
}
一个
TARGET_EXPR
代表一个临时对象。其第一个操作数是表示这个临时对象的一个
VAR_DECL
。其第二个操作数是该临时对象的初始值。该初始值被评估,然后拷贝入这个临时对象。
一个
AGGR_INIT_EXPR
代表,作为一个函数调用返回值,或一个构造函数结果的初始化。一个
AGGR_INIT_EXPR
将仅出现作为一个
TARGET_EXPR
的第二个操作数。该
AGGR_INIT_EXPR
的第一个操作数是所调用函数的地址,就像在一个
CALL_EXPR
那样。第二个操作数是以一个
TREE_LIST
形式传递给该函数的实参,同样类似于在一个
CALL_EXPR
里那样。这个表达式的值由这个函数返回。
那么如果
AGGR_INIT_EXPR
被用于
TRAGET_EXPR
里,递归
break_out_target_exprs
来拷贝这个节点。对于这个拷贝过来的表达式,
build_cplus_new
为其初始化产生了代码。
2007
tree
2008
build_cplus_new
(tree type, tree init)
in
cp/tree.c
2009
{
2010
tree fn;
2011
tree slot;
2012
tree rval;
2013
int is_ctor;
2014
2015
/*
Make sure that we're not trying to create an instance of an
2016
abstract class.
*/
2017
abstract_virtuals_error
(NULL_TREE, type);
2018
2019
if (TREE_CODE (init) != CALL_EXPR &&
TREE_CODE (init) != AGGR_INIT_EXPR)
2020
return
convert (type, init);
2021
2022
fn = TREE_OPERAND (init, 0);
2023
is_ctor = (TREE_CODE (fn) ==
ADDR_EXPR
2024
&&
TREE_CODE (TREE_OPERAND (fn, 0)) == FUNCTION_DECL
2025
&& DECL_CONSTRUCTOR_P
(TREE_OPERAND (fn, 0)));
2026
2027
slot = build_local_temp
(type);
2028
2029
/*
We split the CALL_EXPR into its function and its arguments here.
2030
Then,
in expand_expr, we put them back together. The reason for
2031
this
is that this expression might be a default argument
2032
expression. In that case, we need a new
temporary every time the
2033
expression is used. That's what
break_out_target_exprs does; it
2034
replaces every AGGR_INIT_EXPR with a copy
that uses a fresh
2035
temporary slot. Then, expand_expr builds up
a call-expression
2036
using
the new slot.
*/
2037
2038
/*
If we don't need to use a constructor to create an object of this
2039
type,
don't mess with AGGR_INIT_EXPR.
*/
2040
if (is_ctor ||
TREE_ADDRESSABLE (type))
2041
{
2042
rval = build
(AGGR_INIT_EXPR, type, fn, TREE_OPERAND (init, 1), slot);
2043
TREE_SIDE_EFFECTS (rval) =
1;
2044
AGGR_INIT_VIA_CTOR_P
(rval) = is_ctor;
2045
}
2046
else
2047
rval = init;
2048
2049
rval = build_target_expr
(slot, rval);
2050
2051
return
rval;
2052
}
现在需要更新在
TARGET_EXPR
中的临时对象,因为我们不是在产生原始的
TARGET_EXPR
的上下文中。看到这个临时对象成为了局部的,其
DECL_CONTEXT
被强制设置为
current_function_decl
。
253
static
tree
254
build_local_temp
(tree type)
in
cp/tree.c
255
{
256
tree slot = build_decl
(VAR_DECL, NULL_TREE, type);
257
DECL_ARTIFICIAL (slot) = 1;
258
DECL_CONTEXT (slot) = current_function_decl
;
259
layout_decl
(slot,
0);
260
return
slot;
261
}
注意如果对于这个
AGGR_INIT_EXPR
,
AGGR_INIT_VIA_CTOR_P
成立,表示这个初始化是通过一个构造函数调用实现。在
2042
行所构建的
AGGR_INIT_EXPR
,其第三个操作数是这个临时对象,它总是一个
VAR_DECL
。而
init
是原来
TARGET_EXPR
中的
AGGR_INIT_EXPR
(这个
AGGR_INIT_EXPR
的第三个操作数的地址被提取,并替换实参列表中的值。这个情况下,该表达式的值是提供给这个
AGGR_INIT_EXPR
的
VAR_DECL
;构造函数不返回值)。
最后,这个新构建的
TARGET_EXPR
被
build_cplus_new
返回。
而对于
TARGET_EXPR
的第二个操作数不是
AGGR_INIT_EXPR
的情况,该操作数由
build_target_expr_with_type
来处理。这里如果
init
是
TARGET_EXPR
,它必定是由在
bot_manip
中
1209
行的
break_out_target_exprs
所构建的,这个节点正是我们期望的。
320
tree
321
build_target_expr_with_type
(tree init,
tree type)
in
cp/tree.c
322
{
323
tree slot;
324
325
if (TREE_CODE (init) == TARGET_EXPR)
326
return
init;
327
else if (CLASS_TYPE_P (type) &&
!TYPE_HAS_TRIVIAL_INIT_REF (type)
328
&& TREE_CODE (init) !=
COND_EXPR
329
&& TREE_CODE (init) !=
CONSTRUCTOR
330
&& TREE_CODE (init) !=
VA_ARG_EXPR)
331
/* We need to
build up a copy constructor call. COND_EXPR is a special
332
case because we
already have copies on the arms and we don't want
333
another one
here. A CONSTRUCTOR is aggregate initialization, which
334
is handled
separately. A VA_ARG_EXPR is magic creation of an
335
aggregate; there's no additional work to be
done.
*/
336
return
force_rvalue
(init);
337
338
slot = build_local_temp
(type);
339
return
build_target_expr
(slot, init);
340
}
上面,如果
TYPE_HAS_TRIVIAL_INIT_REF
不是
0
,表示该拷贝初始化可以使用按位拷贝。对于这样的情形,可以简单地构建
TARGET_EXPR
;否则,需要执行一个左值到右值的转换,包括如下所示的拷贝构造函数调用。
590
tree
591
force_rvalue
(tree expr)
in
cvt.c
592
{
593
if (IS_AGGR_TYPE (TREE_TYPE
(expr)) && TREE_CODE (expr) != TARGET_EXPR)
594
expr = ocp_convert (TREE_TYPE (expr), expr,
595
CONV_IMPLICIT|CONV_FORCE_TEMP, LOOKUP_NORMAL);
596
else
597
expr = decay_conversion
(expr);
598
599
return
expr;
600
}
不久之后我们将看到
ocp_convert
的细节。在这里总而言之,该函数将产生调用合适的拷贝构造函数代码,然后调用
build_cplus_new
来产生构建这个临时对象及其初始化。在离开
bot_manip
之前,看一下
build_target_expr
。
234
static
tree
235
build_target_expr
(tree decl, tree
value)
in
cp/tree.c
236
{
237
tree t;
238
239
t = build
(TARGET_EXPR,
TREE_TYPE (decl), decl, value,
240
cxx_maybe_build_cleanup
(decl), NULL_TREE);
241
/* We always set
TREE_SIDE_EFFECTS so that expand_expr does not
242
ignore the
TARGET_EXPR. If there really turn out to be no
243
side-effects,
then the optimizer should be able to get rid of
244
whatever code is
generated anyhow.
*/
245
TREE_SIDE_EFFECTS (t) = 1;
246
247
return
t;
248
}
对于具有非平凡析构函数的临时对象,编译器需要产生代码,在其越出其作用域时,通过调用这个析构函数摧毁这个临时对象。因此在
240
行,
cxx_maybe_build_cleanup
产生这些代码,如果需要的话。
现在在
bot_manip
的
1213
行,
u
是对应于
t
的
TARGET_EXPR
。它把新旧版本的临时对象映射起来。然后,我们立即用新版本取代了旧版本。不过,在由
break_out_target_exprs
处理的节点的某些子节点中,可能仍然保留了这个旧版本的引用,它们需要如下的更新。
1236
static
tree
1237
bot_replace
(tree* t,
in
cp/tree.c
1238
int* walk_subtrees
ATTRIBUTE_UNUSED ,
1239
void* data)
1240
{
1241
splay_tree target_remap =
((splay_tree) data);
1242
1243
if (TREE_CODE (*t) ==
VAR_DECL)
1244
{
1245
splay_tree_node n =
splay_tree_lookup (target_remap,
1246
(splay_tree_key)
*t);
1247
if (n)
1248
*t = (tree) n->value;
1249
}
1250
1251
return
NULL_TREE;
1252
}
回到
convert_default_arg
,在
4274
行从
break_out_target_exprs
得到这个更新的
arg
,然后接下来的函数用于产生初始化代码。
5.12.5.2.2.2.3.
省略实参
最后一个的可能就是省略实参,注意到省略实参与缺省实参不可共存。在前端中,为了识别包含了省略实参的函数声明,形参列表由
NULL
,而不是
void_list_node
来结尾。
4161
tree
4162
convert_arg_to_ellipsis
(tree arg)
in
call.c
4163
{
4164
/*
[expr.call]
4165
4166
The
lvalue-to-rvalue, array-to-pointer, and function-to-pointer
4167
standard conversions are performed.
*/
4168
arg = decay_conversion
(arg);
4169
/* [expr.call]
4170
4171
If the
argument has integral or enumeration type that is subject
4172
to the
integral promotions (_conv.prom_), or a floating point
4173
type
that is subject to the floating point promotion
4174
(_conv.fpprom_), the value of the argument
is converted to the
4175
promoted type before the call.
*/
4176
if (TREE_CODE (TREE_TYPE
(arg)) == REAL_TYPE
4177
&&
(TYPE_PRECISION (TREE_TYPE (arg))
4178
< TYPE_PRECISION
(double_type_node)))
4179
arg = convert_to_real
(double_type_node, arg);
4180
else if (INTEGRAL_OR_ENUMERATION_TYPE_P
(TREE_TYPE (arg)))
4181
arg =
perform_integral_promotions (arg);
4182
4183
arg = require_complete_type
(arg);
4184
4185
if (arg != error_mark_node
4186
&& !pod_type_p
(TREE_TYPE (arg)))
4187
{
4188
/*
Undefined behavior [expr.call] 5.2.2/7. We used
to just warn
4189
here
and do a bitwise copy, but now cp_expr_size will abort if we
4190
try
to do that.
4191
If
the call appears in the context of a sizeof expression,
4192
there is no need to emit a warning, since
the expression won't be
4193
evaluated. We keep the builtin_trap just
as a safety check.
*/
4194
if (!skip_evaluation
)
4195
warning ("cannot
pass objects of non-POD type `%#T' through `...'; "
4196
"call will
abort at runtime", TREE_TYPE (arg));
4197
arg = call_builtin_trap
();
4198
arg = build
(COMPOUND_EXPR, integer_type_node, arg,
4199
integer_zero_node);
4200
}
4201
4202
return
arg;
4203
}
【
3
】的条文
5.2.2
“函数调用”,条款
7
如下定义了编译器在省略实参上的行为。
7. 当一个给定的实参没有对应的形参,该实参被以这样的方式传入——接受函数可以通过调用 va_arg ( 18.7 )来得到该实参值。左值到右值( 4.1 ),数组到指针( 4.2 ),及函数到指针( 4.3 )标准转换在该实参表达式上执行。在这些转换之后,如果该实参不是数字,枚举值,指针,成员的指针,或类类型,该程序非法。如果该实参具有一个 non-POD 类类型(条文 9 ),其行为是未定义的。如果该实参具有适用于整型提升( 4.5 )的整型或枚举类型,或适用于浮点提升( 4.6 )的浮点类型,在该调用之前,该实参的值被转换到提升后的类型。这些提升被称为缺省实参提升 。 |
decay_conversion
执行在
exp
中的转换,它应用在当一个左值出现在一个右值上下文中时候。包括左值到右值,数组到指针,及函数到指针的转换。
1335
tree
1336
decay_conversion
(tree exp)
n
typeck.c
1337
{
1338
tree type;
1339
enum
tree_code code;
1340
1341
type = TREE_TYPE (exp);
1342
code = TREE_CODE (type);
1343
1344
if (code == REFERENCE_TYPE)
1345
{
1346
exp = convert_from_reference
(exp);
1347
type = TREE_TYPE (exp);
1348
code = TREE_CODE (type);
1349
}
1350
1351
if (type == error_mark_node)
1352
return
error_mark_node;
1353
1354
if (type_unknown_p (exp))
1355
{
1356
cxx_incomplete_type_error
(exp, TREE_TYPE (exp));
1357
return
error_mark_node;
1358
}
1359
1360
/*
Constants can be used directly unless they're not loadable.
*/
1361
if (TREE_CODE (exp) ==
CONST_DECL)
1362
exp = DECL_INITIAL (exp);
1363
/*
Replace a nonvolatile const static variable with its value. We
1364
don't
do this for arrays, though; we want the address of the
1365
first
element of the array, not the address of the first element
1366
of its
initializing constant.
*/
1367
else if (code != ARRAY_TYPE)
1368
{
1369
exp = decl_constant_value
(exp);
1370
type = TREE_TYPE (exp);
1371
}
1372
1373
/* build_c_cast puts on a NOP_EXPR to make the
result not an lvalue.
1374
Leave
such NOP_EXPRs, since RHS is being used in non-lvalue context.
*/
1375
1376
if (code == VOID_TYPE)
1377
{
1378
error ("void value
not ignored as it ought to be");
1379
return
error_mark_node;
1380
}
1381
if
(invalid_nonstatic_memfn_p (exp))
1382
return
error_mark_node;
1383
if (code == FUNCTION_TYPE ||
is_overloaded_fn
(exp))
1384
return
build_unary_op (ADDR_EXPR, exp, 0);
1385
if (code == ARRAY_TYPE)
1386
{
1387
tree adr;
1388
tree ptrtype;
1389
1390
if (TREE_CODE (exp) ==
INDIRECT_REF)
1391
return
build_nop (build_pointer_type
(TREE_TYPE
(type)),
1392
TREE_OPERAND (exp, 0));
1393
1394
if (TREE_CODE (exp) ==
COMPOUND_EXPR)
1395
{
1396
tree op1 = decay_conversion
(TREE_OPERAND (exp, 1));
1397
return
build
(COMPOUND_EXPR, TREE_TYPE (op1),
1398
TREE_OPERAND
(exp, 0), op1);
1399
}
1400
1401
if (!lvalue_p (exp)
1402
&& ! (TREE_CODE
(exp) == CONSTRUCTOR && TREE_STATIC (exp)))
1403
{
1404
error ("invalid use
of non-lvalue array");
1405
return
error_mark_node;
1406
}
1407
1408
ptrtype = build_pointer_type
(TREE_TYPE (type));
1409
1410
if (TREE_CODE (exp) ==
VAR_DECL)
1411
{
1412
if
(!cxx_mark_addressable (exp))
1413
return
error_mark_node;
1414
adr = build_nop
(ptrtype, build_address
(exp));
1415
TREE_SIDE_EFFECTS (adr)
= 0;
/*
Default would be, same as EXP.
*/
1416
return
adr;
1417
}
1418
/*
This way is better for a COMPONENT_REF since it can
1419
simplify the offset for a component.
*/
1420
adr = build_unary_op
(ADDR_EXPR, exp, 1);
1421
return
cp_convert (ptrtype, adr);
1422
}
1423
1424
/*
[basic.lval]: Class rvalues can have cv-qualified types; non-class
1425
rvalues always have cv-unqualified
types.
*/
1426
if (! CLASS_TYPE_P (type))
1427
exp = cp_convert
(TYPE_MAIN_VARIANT (type), exp);
1428
1429
return
exp;
1430
}
首先对于
REFERENCE_TYPE
,它是一个左值,要把它转换为右值,就应该使用被引用的值,而不再保存引用。在前端中,
INDIRECT_REF
为这个目标而构建(这是因为引用总是传入其地址就像指针那样;而在编译器中,引用的模式(
mode
)与指针的相同,都是
ptr_mode
,参见
build_reference_type
)。
566
tree
567
convert_from_reference
(tree val)
in cvt.c
568
{
569
if (TREE_CODE (TREE_TYPE (val)) ==
REFERENCE_TYPE)
570
return
build_indirect_ref
(val, NULL);
571
return
val;
572
}
记得指针是右值。当处理
ARRAY_TYPE
时,如果我们正在使用形如:
int *a[i]
(
a
是维度大于
1
的数组,例如,
int
A[2][2]
)的表达式,满足
1390
行的条件,
因此构建了形如:
int**
的类型,并且看到转换指针“
int
*a[i]
”到“
int**
”不需要产生任何代码,因而为这个转换构建了
NOP_EXPR
。
而如果
exp
只是一个数组的声明,例如:
int a[8]
,这个声明的右值是“
int*
”。可以直接构建这个右值。不过,对于其他的情形,例如:“
tempA.a
”(这是一棵以
SCOPE_REF
为根节点的树),就没有可以直接使用的简单规则,因此调用
build_unary_op
及
cp_convert
来执行合适的转换。
正如在
convert_arg_to_ellipsis
的
4188
行的注释所提到的,对于对应于
non-POD
类类型的未定义行为,
GCC
以前使用按位拷贝,不过在当前版本中,这个行为在后面对
cp_expr_size
的调用将导致异常终止。这个终止由下面“
__builtin_trap
”的调用触发,在
4198
行它成为了
COMPOUND_EXPR
形式的
arg
的一部分。
4148
c
tree
4149
call_builtin_trap
(void)
in
call.c
4150
{
4151
tree fn = IDENTIFIER_GLOBAL_VALUE
(get_identifier ("__builtin_trap"));
4152
4153
my_friendly_assert (fn !=
NULL, 20030927);
4154
fn = build_call (fn,
NULL_TREE);
4155
return
fn;
4156
}
内建陷阱的行为是:如果目标机器定义了陷阱指令,就使用它;否则编译器将调用
abort
()
(参考
expand_builtin_trap
)。
下面是对
non-POD
类类型省略实参的一个有趣的测试:
#include
<stdarg.h>
class
A {
public
: virtual
void func() {}
};
int func (int
a, ...) {
va_list ap;
va_start(ap, a)
;
va_arg(ap, A);
va_end(ap);
return
1;
}
int main() {
A a;
func (1, a);
// sizeof (func (1, a))
}
编译器将给出以下警告:
test2.cpp: In function ‘int func(int, ...)’:
test2.cpp:11: warning: cannot receive objects of
non-POD type ‘class A’ through ‘...’; call will abort at runtime
test2.cpp: In function ‘int main()’:
test2.cpp:19: warning: cannot pass objects of non-POD
type ‘class A’ through ’...’; call will abort at runtime
当执行这个程序时,得到错误:
Illegal instruction.
不过,如果我们使用注释中的语句,编译器将给出警告:
test2.cpp: In function ‘int func(int, ...)’:
test2.cpp:11: warning: cannot receive objects of
non-POD type ‘class A’ through ‘...’; call will abort at runtime
而在执行时,没有产生错误,因为
func(1, a)
没有被评估,除了其返回值。
相关文章推荐
- GCC-3.4.6源代码学习笔记(6)
- GCC-3.4.6源代码学习笔记(166)
- GCC-3.4.6源代码学习笔记(139-续2)
- GCC-3.4.6源代码学习笔记(28)
- GCC-3.4.6源代码学习笔记 当前目录
- GCC-3.4.6源代码学习笔记(116)
- GCC-3.4.6源代码学习笔记(62)
- GCC-3.4.6源代码学习笔记(34)
- GCC-3.4.6源代码学习笔记(47)
- GCC-3.4.6源代码学习笔记(170)
- GCC-3.4.6源代码学习笔记(64)
- GCC-3.4.6源代码学习笔记(15)
- GCC-3.4.6源代码学习笔记(155)
- GCC-3.4.6源代码学习笔记(138)
- GCC-3.4.6源代码学习笔记(111)
- GCC-3.4.6源代码学习笔记(88)
- GCC-3.4.6源代码学习笔记(60)
- GCC-3.4.6源代码学习笔记(167)
- GCC-3.4.6源代码学习笔记(149)
- GCC-3.4.6源代码学习笔记(174)