Studying note of GCC-3.4.6 source (152)

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.

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 .

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.

 

你可能感兴趣的:(Studying note of GCC-3.4.6 source (152))