Studying note of GCC-3.4.6 source (152)
2011-01-29 15:07
337 查看
5.12.5.2.2.2.2.
Default arguments
If arg
exhausted, but parm
doesn’t, parm
must contain default arguments, for which the list should be terminated by the
special void_list_node
.
Then in corresponding node, TREE_VALUE holds the type, and TREE_PURPOSE is the
expression of the default argument.
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
}
Node of DEFAULT_ARG is created for unparsed default argument.
Remember during parsing class definition, any default argument is cached by
DEFAULT_ARG, which will be parsed after the parsing. So DEFAULT_ARG should not
be present here (if so, it may be missing “};” in the class definition), and at
this point, the front-end doesn’t know how to handle node of this kind.
Then arg
passed for below function at line 4274, is the default argument shared by all
invocations of the function. However, as [3] defines, “
Default arguments are evaluated each time the
function is called
”, so before really evaluate the expression of the
default argument, it needs prepare appropriate copy of arg
and update this local
temparories acclaimed as below.
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
}
Above walk_tree
traverses the subtree rooted by t
and
executes provided function upon nodes selected by the code of t
. In
first traversal, below function is used. Note that the function always returns
NULL (copy_tree_r
below returns NULL) to force walk_tree
to do full traversal with depth
first (i.e. tp
maybe a tree_list, in which node can contain operands which in turn can be
tree_list again and so on, the visit should be from bottom up); however whether
stepping into the sub-tree (i.e. operand) is controlled by local variable walk_subtrees
(which is passes as argument walk_subtrees
in below function). Before
processing the tree node with the specified function, walk_subtrees
is set as 1; it is
the specified function to decide whether the node is of interesting and needs
to be stepped into if no result gotten.
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
}
For node other than constant or TRAGET_EXPR, copy_tree_r
copies the node
if it is either *_CST (see it is filtered out by TREE_CONSTANT above) or
expression or TREE_LIST or TREE_VEC or OVERLOAD (told by C++ hook of tree_chain_matters_p
below, and note that walk_subtrees
is unchanged here, walk_tree
will step into the sub-nodes of the node and continues copying the structure
accordingly).
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
}
A TARGET_EXPR represents a temporary object. The first operand is a
VAR_DECL for the temporary variable. The second operand is the initializer for
the temporary. The initializer is evaluated, and copied (bitwise) into the
temporary.
An AGGR_INIT_EXPR represents the initialization as the return value
of a function call, or as the result of a constructor. An AGGR_INIT_EXPR will
only appear as the second operand of a TARGET_EXPR. The first operand to the
AGGR_INIT_EXPR is the address of a function to call, just as in a CALL_EXPR.
The second operand are the arguments to pass that function, as a TREE_LIST,
again in a manner similar to that of a CALL_EXPR. The value of the expression
is that returned by the function.
Then if AGGR_INIT_EXPR is used in TRAGET_EXPR, it recurses break_out_target_exprs
to copy this node. With the copied expression, build_cplus_new
generates
code for the initialization.
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
}
Now it needs update the temporary in the TARGET_EXPR, as we are not
within the context generating the original TARGET_EXPR. See it makes the
temporary as local by forcing its DECL_CONTEXT by 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
}
Note if AGGR_INIT_VIA_CTOR_P holds for the AGGR_INIT_EXPR, then the
initialization is via a constructor call. AGGR_INIT_EXPR constructed at line 2042
will have this temporary as its third operand, which is always a VAR_DECL. And init
is
the AGGR_INIT_EXPR in the original TARGET_EXPR (the address of the third
operand of the AGGR_INIT_EXPR is taken, and this value replaces the first
argument in the argument list. In this case, the value of the expression is the
VAR_DECL given by the third operand to the AGGR_INIT_EXPR; constructors do not
return a value).
Finally, this new generated TARGET_EXPR is retuned by build_cplus_new
.
While for second operand in TARGET_EXPR other than AGGR_INIT_EXPR,
the operand is handled by build_target_expr_with_type
instead. Here if init
is
TARGET_EXPR, it must be one that built by break_out_target_exprs
at line 1209 in bot_manip
, which is the node we expect.
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
}
Above if TYPE_HAS_TRIVIAL_INIT_REF is nonzero, it means that copy
initialization of the type can use a bitwise copy. For which case, it can
simply build TARGET_EXPR; otherwise, it needs perform an lvalue-to-rvalue
conversion, including invoking the copy ctor as below.
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
}
We will see the detail of ocp_convert
in short later. As summary here,
the function will generate code for invoking the appropriate copy ctor and then
invoking build_cplus_new
to create the temporary with its initialization. Before leaving bot_manip
,
it worthes a look of 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
}
For temporary with non-trivial destructor, the compiler needs
generate code to destory the temporary at exitting its scope by invoking the
destructor. So at line 240, cxx_maybe_build_cleanup
generates this code
if necessary.
Now at line 1213 in
bot_manip
,
u
is
the TARGET_EXPR built corresponding to t
. It maps the new version temporary with the old
one. Then, we immediately replace the old one with it. However, it is possible
some sub-nodes within node handled by break_out_target_exprs
, still hold the
reference to this old version, which needs be updated as below.
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
}
Back convert_default_arg
, it gets the updated arg
at
line 4274 from break_out_target_exprs
, then the following functions are used
to generate code for the initialization.
5.12.5.2.2.2.3.
Ellipsis arguments
The last possibility is the ellipsis arguments, note that ellipsis
argument can’t coexist with default argument. In front-end to tell out function
declaration containing ellipsis arguments, the list of parameters is terminated
by NULL instead of 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], clause 5.2.2
“Function call”, terms 7 defines the behavior of compiler upon ellipsis
argument as below.
Function decay_conversion
performs the conversions in exp,
which are used when an lvalue appears in an
rvalue context, and include lvalue-to-rvalue, array-to-pointer, and function-to-pointer
conversion.
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
}
First for REFERENCE_TYPE, it is a lvalue, to change it to rvalue, it
should uses the value referred instead of holding reference any more. In
front-end, INDIRECT_REF is built for the purpose (it because reference always
has its address passed just like pointer, and in compiler, the mode of
reference is the same as pointer, both are ptr_mode
, see 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
}
Remember pointer is rvalue. When handling ARRAY_TYPE, condition at
line 1390 is satisfied, if we are using expression like: int *a[i] (a is array
having dimensions more than 1, for example, int A[2][2]), so it builds type
like: int**, and see converting pointer of “int *a[i]” to “int**” needesn’t
generate any code, NOP_EXPR is built for such conversion.
Further if exp
simply is a declaration of an array, for
exmaple: int a[8], rvalue of this declaration is just “int*”. It can build this
rvalue directly. However, for other cases, for example: tempA.a (it is a tree
rooted by SCOPE_REF), it is not a simple rule we can use directly, so it asks build_unary_op
and cp_convert
to do the appropriate conversion.
As comment at line 4188 in
convert_arg_to_ellipsis
mentions, for the undefined behavior of non-POD class type, GCC once uses
bitwise copy, but this behavior will cause later invocation of cp_expr_size
triggering abort in current version. This abort is triggered by below
invocation of “__builtin_trap”, which is included in arg
of COMPOUND_EXPR at line 4198.
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
}
The behavior of the builtin trap will use trap if the target machine
defines it; otherwise the compiler will call abort
() (see expand_builtin_trap
).
Below is an
interesting test for the behavior of non-POD class type:
#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))
}
The compiler will give out
following warning:
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
When execute the program, it gets error: Illegal instruction.
However, if we use statement in comment instead, the compiler then
gives warning as:
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
And at execution, no error will be output, as func(1, a) will not be
evaluated except its returned value.
Default arguments
If arg
exhausted, but parm
doesn’t, parm
must contain default arguments, for which the list should be terminated by the
special void_list_node
.
Then in corresponding node, TREE_VALUE holds the type, and TREE_PURPOSE is the
expression of the default argument.
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
}
Node of DEFAULT_ARG is created for unparsed default argument.
Remember during parsing class definition, any default argument is cached by
DEFAULT_ARG, which will be parsed after the parsing. So DEFAULT_ARG should not
be present here (if so, it may be missing “};” in the class definition), and at
this point, the front-end doesn’t know how to handle node of this kind.
Then arg
passed for below function at line 4274, is the default argument shared by all
invocations of the function. However, as [3] defines, “
Default arguments are evaluated each time the
function is called
”, so before really evaluate the expression of the
default argument, it needs prepare appropriate copy of arg
and update this local
temparories acclaimed as below.
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
}
Above walk_tree
traverses the subtree rooted by t
and
executes provided function upon nodes selected by the code of t
. In
first traversal, below function is used. Note that the function always returns
NULL (copy_tree_r
below returns NULL) to force walk_tree
to do full traversal with depth
first (i.e. tp
maybe a tree_list, in which node can contain operands which in turn can be
tree_list again and so on, the visit should be from bottom up); however whether
stepping into the sub-tree (i.e. operand) is controlled by local variable walk_subtrees
(which is passes as argument walk_subtrees
in below function). Before
processing the tree node with the specified function, walk_subtrees
is set as 1; it is
the specified function to decide whether the node is of interesting and needs
to be stepped into if no result gotten.
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
}
For node other than constant or TRAGET_EXPR, copy_tree_r
copies the node
if it is either *_CST (see it is filtered out by TREE_CONSTANT above) or
expression or TREE_LIST or TREE_VEC or OVERLOAD (told by C++ hook of tree_chain_matters_p
below, and note that walk_subtrees
is unchanged here, walk_tree
will step into the sub-nodes of the node and continues copying the structure
accordingly).
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
}
A TARGET_EXPR represents a temporary object. The first operand is a
VAR_DECL for the temporary variable. The second operand is the initializer for
the temporary. The initializer is evaluated, and copied (bitwise) into the
temporary.
An AGGR_INIT_EXPR represents the initialization as the return value
of a function call, or as the result of a constructor. An AGGR_INIT_EXPR will
only appear as the second operand of a TARGET_EXPR. The first operand to the
AGGR_INIT_EXPR is the address of a function to call, just as in a CALL_EXPR.
The second operand are the arguments to pass that function, as a TREE_LIST,
again in a manner similar to that of a CALL_EXPR. The value of the expression
is that returned by the function.
Then if AGGR_INIT_EXPR is used in TRAGET_EXPR, it recurses break_out_target_exprs
to copy this node. With the copied expression, build_cplus_new
generates
code for the initialization.
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
}
Now it needs update the temporary in the TARGET_EXPR, as we are not
within the context generating the original TARGET_EXPR. See it makes the
temporary as local by forcing its DECL_CONTEXT by 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
}
Note if AGGR_INIT_VIA_CTOR_P holds for the AGGR_INIT_EXPR, then the
initialization is via a constructor call. AGGR_INIT_EXPR constructed at line 2042
will have this temporary as its third operand, which is always a VAR_DECL. And init
is
the AGGR_INIT_EXPR in the original TARGET_EXPR (the address of the third
operand of the AGGR_INIT_EXPR is taken, and this value replaces the first
argument in the argument list. In this case, the value of the expression is the
VAR_DECL given by the third operand to the AGGR_INIT_EXPR; constructors do not
return a value).
Finally, this new generated TARGET_EXPR is retuned by build_cplus_new
.
While for second operand in TARGET_EXPR other than AGGR_INIT_EXPR,
the operand is handled by build_target_expr_with_type
instead. Here if init
is
TARGET_EXPR, it must be one that built by break_out_target_exprs
at line 1209 in bot_manip
, which is the node we expect.
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
}
Above if TYPE_HAS_TRIVIAL_INIT_REF is nonzero, it means that copy
initialization of the type can use a bitwise copy. For which case, it can
simply build TARGET_EXPR; otherwise, it needs perform an lvalue-to-rvalue
conversion, including invoking the copy ctor as below.
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
}
We will see the detail of ocp_convert
in short later. As summary here,
the function will generate code for invoking the appropriate copy ctor and then
invoking build_cplus_new
to create the temporary with its initialization. Before leaving bot_manip
,
it worthes a look of 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
}
For temporary with non-trivial destructor, the compiler needs
generate code to destory the temporary at exitting its scope by invoking the
destructor. So at line 240, cxx_maybe_build_cleanup
generates this code
if necessary.
Now at line 1213 in
bot_manip
,
u
is
the TARGET_EXPR built corresponding to t
. It maps the new version temporary with the old
one. Then, we immediately replace the old one with it. However, it is possible
some sub-nodes within node handled by break_out_target_exprs
, still hold the
reference to this old version, which needs be updated as below.
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
}
Back convert_default_arg
, it gets the updated arg
at
line 4274 from break_out_target_exprs
, then the following functions are used
to generate code for the initialization.
5.12.5.2.2.2.3.
Ellipsis arguments
The last possibility is the ellipsis arguments, note that ellipsis
argument can’t coexist with default argument. In front-end to tell out function
declaration containing ellipsis arguments, the list of parameters is terminated
by NULL instead of 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], clause 5.2.2
“Function call”, terms 7 defines the behavior of compiler upon ellipsis
argument as below.
7. When there is no parameter for a given argument, the argument is passed in such a way that the receiving function can obtain the value of the argument by invoking va_arg (18.7). The lvalue-to-rvalue (4.1), array-to-pointer (4.2), and function-to-pointer (4.3) standard conversions are performed on the argument expression. After these conversions, if the argument does not have arithmetic, enumeration, pointer, pointer to member, or class type, the program is ill-formed. If the argument has a non-POD class type (clause 9), the behavior is undefined. If the argument has integral or enumeration type that is subject to the integral promotions (4.5), or a floating point type that is subject to the floating point promotion (4.6), the value of the argument is converted to the promoted type before the call. These promotions are referred to as the default argument promotions . |
performs the conversions in exp,
which are used when an lvalue appears in an
rvalue context, and include lvalue-to-rvalue, array-to-pointer, and function-to-pointer
conversion.
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
}
First for REFERENCE_TYPE, it is a lvalue, to change it to rvalue, it
should uses the value referred instead of holding reference any more. In
front-end, INDIRECT_REF is built for the purpose (it because reference always
has its address passed just like pointer, and in compiler, the mode of
reference is the same as pointer, both are ptr_mode
, see 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
}
Remember pointer is rvalue. When handling ARRAY_TYPE, condition at
line 1390 is satisfied, if we are using expression like: int *a[i] (a is array
having dimensions more than 1, for example, int A[2][2]), so it builds type
like: int**, and see converting pointer of “int *a[i]” to “int**” needesn’t
generate any code, NOP_EXPR is built for such conversion.
Further if exp
simply is a declaration of an array, for
exmaple: int a[8], rvalue of this declaration is just “int*”. It can build this
rvalue directly. However, for other cases, for example: tempA.a (it is a tree
rooted by SCOPE_REF), it is not a simple rule we can use directly, so it asks build_unary_op
and cp_convert
to do the appropriate conversion.
As comment at line 4188 in
convert_arg_to_ellipsis
mentions, for the undefined behavior of non-POD class type, GCC once uses
bitwise copy, but this behavior will cause later invocation of cp_expr_size
triggering abort in current version. This abort is triggered by below
invocation of “__builtin_trap”, which is included in arg
of COMPOUND_EXPR at line 4198.
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
}
The behavior of the builtin trap will use trap if the target machine
defines it; otherwise the compiler will call abort
() (see expand_builtin_trap
).
Below is an
interesting test for the behavior of non-POD class type:
#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))
}
The compiler will give out
following warning:
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
When execute the program, it gets error: Illegal instruction.
However, if we use statement in comment instead, the compiler then
gives warning as:
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
And at execution, no error will be output, as func(1, a) will not be
evaluated except its returned value.
相关文章推荐
- Studying note of GCC-3.4.6 source (5)
- Studying note of GCC-3.4.6 source (139)
- Studying note of GCC-3.4.6 source (13)
- Studying note of GCC-3.4.6 source (149)
- Studying note of GCC-3.4.6 source (159)
- Studying note of GCC-3.4.6 source (164)
- Studying note of GCC-3.4.6 source (26 cont1)
- Studying note of GCC-3.4.6 source (167)
- Studying note of GCC-3.4.6 source (52)
- Studying note of GCC-3.4.6 source (83)
- Studying note of GCC-3.4.6 source (88)
- Studying note of GCC-3.4.6 source (113)
- Studying note of GCC-3.4.6 source (127)
- Studying note of GCC-3.4.6 source (129)
- Studying note of GCC-3.4.6 source (160)
- Studying note of GCC-3.4.6 source (28)
- Studying note of GCC-3.4.6 source (30)
- Studying note of GCC-3.4.6 source (36)
- Studying note of GCC-3.4.6 source (37)
- Studying note of GCC-3.4.6 source (175)