GCC-3.4.6源代码学习笔记(163)

5.13.4.7.1.2.  为实参执行适合的转换

在这一节中,我们要深入一些来看一下如何把实参匹配形参,并执行必须的转换。

C++ 前端中,函数形参被记录在 FUNCTION_TYPE TYPE_ARG_TYPES 域中,它是一个 tree_list 类型的串,其节点中的 TREE_VALUE 是对应形参的类型; TREE_PURPOSE 是缺省实参值的一个表达式,如果有的话。如果在该链表中最后的节点是 void_list_node (一个 TREE_LIST 节点,其中的 TREE_VALUE void_type_node ),那么这个类型的函数不接受可变数目参数。否则,它接受可变数目参数。并且该函数的实参将被构建成一个 tree_list 链表,其节点中的 TREE_VALUE 是一个赋值表达式的一个表示。

在下面的函数中,参数 typelist 是用于形参的 tree_list ,而 values 是用于实参的 tree_list 。因此在不使用可变数目参数的情形下( typelist void_list_node 结尾), typelist values 的长度相同(这时,若有缺省实参,它被加 values 中)。 2573 行的条件过滤出错误的情形,因为我们以相同的速率加入 typelist values

确定转换序列及转换的真正执行的细节,由 预备知识—转换的细节 一节给出。

 

2538 tree

2539 convert_arguments (tree typelist, tree values, tree fndecl, int flags)                in typeck.c

2540 {

2541    tree typetail, valtail;

2542    tree result = NULL_TREE;

2543    const char *called_thing = 0;

2544    int i = 0;

2545

2546     /* Argument passing is always copy-initialization.  */

2547    flags |= LOOKUP_ONLYCONVERTING;

2548

2549    if (fndecl)

2550    {

2551      if (TREE_CODE (TREE_TYPE (fndecl)) == METHOD_TYPE)

2552      {

2553        if (DECL_NAME (fndecl) == NULL_TREE

2554           || IDENTIFIER_HAS_TYPE_VALUE (DECL_NAME (fndecl)))

2555          called_thing = "constructor";

2556         else

2557          called_thing = "member function";

2558      }

2559      else

2560        called_thing = "function";

2561    }

2562

2563    for (valtail = values, typetail = typelist;

2564        valtail;

2565        valtail = TREE_CHAIN (valtail), i++)

2566     {

2567      tree type = typetail ? TREE_VALUE (typetail) : 0;

2568      tree val = TREE_VALUE (valtail);

2569

2570      if (val == error_mark_node)

2571        return error_mark_node;

2572

2573      if (type == void_type_node)

2574      {

2575        if (fndecl)

2576        {

2577          cp_error_at ("too many arguments to %s `%+#D'", called_thing,

2578                     fndecl);

2579          error ("at this point in file");

2580        }

2581        else

2582          error ("too many arguments to function");

2583        /* In case anybody wants to know if this argument

2584          list is valid.  */

2585        if (result)

2586          TREE_TYPE (tree_last (result)) = error_mark_node;

2587        break ;

2588      }

2589

2590      /* build_c_cast puts on a NOP_EXPR to make the result not an lvalue.

2591        Strip such NOP_EXPRs, since VAL is used in non-lvalue context.  */

2592      if (TREE_CODE (val) == NOP_EXPR

2593         && TREE_TYPE (val) == TREE_TYPE (TREE_OPERAND (val, 0))

2594         && (type == 0 || TREE_CODE (type) != REFERENCE_TYPE))

2595        val = TREE_OPERAND (val, 0);

2596

2597      if (type == 0 || TREE_CODE (type) != REFERENCE_TYPE)

2598      {

2599        if (TREE_CODE (TREE_TYPE (val)) == ARRAY_TYPE

2600           || TREE_CODE (TREE_TYPE (val)) == FUNCTION_TYPE

2601           || TREE_CODE (TREE_TYPE (val)) == METHOD_TYPE)

2602          val = decay_conversion (val);

2603      }

2604

2605      if (val == error_mark_node)

2606        return error_mark_node;

2607

2608      if (type != 0)

2609      {

2610        /* Formal parm type is specified by a function prototype.  */

2611        tree parmval;

2612

2613        if (!COMPLETE_TYPE_P (complete_type (type)))

2614        {

2615          if (fndecl)

2616            error ("parameter %P of `%D' has incomplete type `%T'",

2617                  i, fndecl, type);

2618          else

2619            error ("parameter %P has incomplete type `%T'", i, type);

2620          parmval = error_mark_node;

2621        }

2622         else

2623        {

2624          parmval = convert_for_initialization

2625                                    (NULL_TREE, type, val, flags,

2626                                     "argument passing", fndecl, i);

2627          parmval = convert_for_arg_passing (type, parmval);

2628        }

2629

2630        if (parmval == error_mark_node)

2631          return error_mark_node;

2632

2633        result = tree_cons (NULL_TREE, parmval, result);

2634      }

2635      else

2636      {

2637        if (TREE_CODE (TREE_TYPE (val)) == REFERENCE_TYPE)

2638          val = convert_from_reference (val);

2639

2640        if (fndecl && DECL_BUILT_IN (fndecl)

2641           && DECL_FUNCTION_CODE (fndecl) == BUILT_IN_CONSTANT_P)

2642          /* Don't do ellipsis conversion for __built_in_constant_p

2643            as this will result in spurious warnings for non-POD

2644            types.  */

2645          val = require_complete_type (val);

2646        else

2647          val = convert_arg_to_ellipsis (val);

2648

2649        result = tree_cons (NULL_TREE, val, result);

2650      }

2651

2652      if (typetail)

2653        typetail = TREE_CHAIN (typetail);

2654    }

 

如果 type 是在函数原型中指定的类型,如 2608 行所示,需要确保对应的实参能被转换到该形参的类型,然后执行这个转换(看到处理传入的实参非常类似于把它放在赋值表达式的右手侧)。在这之后, convert_for_arg_passing 为传入的实参,基于目标平台,提供了特定的转换。这里是 convert_for_initialization 的细节。

 

5809 tree

5810 convert_for_initialization (tree exp, tree type, tree rhs, int flags,                     in typeck.c

5811                        const char *errtype, tree fndecl, int parmnum)

5812 {

5813    enum tree_code codel = TREE_CODE (type);

5814    tree rhstype;

5815    enum tree_code coder;

5816

5817    /* build_c_cast puts on a NOP_EXPR to make the result not an lvalue.

5818      Strip such NOP_EXPRs, since RHS is used in non-lvalue context.  */

5819    if (TREE_CODE (rhs) == NOP_EXPR

5820        && TREE_TYPE (rhs) == TREE_TYPE (TREE_OPERAND (rhs, 0))

5821        && codel != REFERENCE_TYPE)

5822      rhs = TREE_OPERAND (rhs, 0);

5823

5824    if (rhs == error_mark_node

5825       || (TREE_CODE (rhs) == TREE_LIST && TREE_VALUE (rhs) == error_mark_node))

5826      return error_mark_node;

5827

5828    if (TREE_CODE (TREE_TYPE (rhs)) == REFERENCE_TYPE)

5829      rhs = convert_from_reference (rhs);

5830

5831    if ((TREE_CODE (TREE_TYPE (rhs)) == ARRAY_TYPE

5832        && TREE_CODE (type) != ARRAY_TYPE

5833        && (TREE_CODE (type) != REFERENCE_TYPE

5834             || TREE_CODE (TREE_TYPE (type)) != ARRAY_TYPE))

5835        || (TREE_CODE (TREE_TYPE (rhs)) == FUNCTION_TYPE

5836           && (TREE_CODE (type) != REFERENCE_TYPE

5837               || TREE_CODE (TREE_TYPE (type)) != FUNCTION_TYPE))

5838        || TREE_CODE (TREE_TYPE (rhs)) == METHOD_TYPE)

5839      rhs = decay_conversion (rhs);

5840

5841    rhstype = TREE_TYPE (rhs);

5842    coder = TREE_CODE (rhstype);

5843

5844    if (coder == ERROR_MARK)

5845      return error_mark_node;

5846

5847     /* We accept references to incomplete types, so we can

5848      return here before checking if RHS is of complete type.  */

5849      

5850    if (codel == REFERENCE_TYPE)

5851    {

5852      /* This should eventually happen in convert_arguments.  */

5853      int savew = 0, savee = 0;

5854

5855      if (fndecl)

5856        savew = warningcount , savee = errorcount ;

5857      rhs = initialize_reference (type, rhs, /*decl=*/ NULL_TREE,

5858                            /*cleanup=*/ NULL);

5859      if (fndecl)

5860      {

5861        if (warningcount > savew)

5862          cp_warning_at ("in passing argument %P of `%+D'", parmnum, fndecl);

5863        else if (errorcount > savee)

5864          cp_error_at ("in passing argument %P of `%+D'", parmnum, fndecl);

5865      }

5866      return rhs;

5867    }     

5868

5869    if (exp != 0)

5870      exp = require_complete_type (exp);

5871    if (exp == error_mark_node)

5872      return error_mark_node;

5873

5874    rhstype = non_reference (rhstype);

5875

5876    type = complete_type (type);

5877

5878    if (IS_AGGR_TYPE (type))

5879      return ocp_convert (type, rhs, CONV_IMPLICIT|CONV_FORCE_TEMP, flags);

5880

5881    return convert_for_assignment (type, rhs, errtype, fndecl, parmnum);

5882 }

 

首先,传入的具有引用类型的实参( rhs )(但对应形参不是)应该拷贝其值,因为该引用应该被忽略,因此需要为这个目的构建一个 INDIRECT_REF 表达式。 5831 行的条件,表示, 1 )我们向一个既不是数组类型或数组引用类型的形参传入一个数组; 2 )传入一个函数(事实上,其地址),但对应的形参即不是函数类型,也不是函数的引用类型;或 3 )传入一个方法(事实上,其地址,并注意到方法不能被引用因为它要求隐含的‘ this ’指针实参);那么把 rhs 根据左值 - - 右值转换衰减。

接着如果形参具有引用类型,传入的实参将被引用而不是被值拷贝。它通过 initialize_reference 以不同于值拷贝的方式处理。在 引用类型的转换 一节中,显示了通过 reference_binding 确定实现从参数到形参的引用类型的转换序列的过程。 reference_binding 将返回 NULL ,如果不存在这样的转换序列;或“ ICS_BAD_FLAG (conv) ”成立,如果该转换不被允许。

 

6120 tree

6121 initialize_reference (tree type, tree expr, tree decl, tree *cleanup)                           in call.c

6122 {

6123    tree conv;

6124

6125    if (type == error_mark_node || error_operand_p (expr))

6126      return error_mark_node;

6127

6128    conv = reference_binding (type, TREE_TYPE (expr), expr, LOOKUP_NORMAL);

6129    if (!conv || ICS_BAD_FLAG (conv))

6130    {

6131      if (!(TYPE_QUALS (TREE_TYPE (type)) & TYPE_QUAL_CONST)

6132          && !real_lvalue_p (expr))

6133        error ("invalid initialization of non-const reference of "

6134             "type '%T' from a temporary of type '%T'",

6135             type, TREE_TYPE (expr));

6136      else

6137         error ("invalid initialization of reference of type "

6138             "'%T' from expression of type '%T'", type,

6139             TREE_TYPE (expr));

6140        return error_mark_node;

6141    }

        … // …

6177    my_friendly_assert (TREE_CODE (conv) == REF_BIND, 20030302);

6178    if (decl)

6179    {

          …

6268      return build_nop (type, expr);

6269    }

6270

6271    /* Perform the conversion.  */

6272    return convert_like (conv, expr);

6273 }

 

上面如果实参 decl 不是 NULL ,它就是由 expr 初始化的 VAR_DECL 。在我们这里的场景中,因为我们正在处理调用实参, decl NULL ,这需要编译器“人工”地构建临时对象。而 6272 行的 convert_like 只是 convert_like_real 的一个简单的封装;可以直接调用这个函数执行这个转换,因为对于引用的形参,我们不需要操心实参的生命周期。

而如果形参不是引用类型,实参应该被拷贝。作为先决条件,对于形参及实参的类型定义必须已经完成。进一步注意到 rhstype 给出了实参的类型,如果我们把一个引用用作实参,引用语义应该被从该类型中丢弃(看到上面我们构建 INDIRECT_REF 来使用实参的值),在这里 non_reference 获取了被援引的类型。

接着如果形参是一个类类型,需要调用相应的构造函数来从实参构建实例,这是上面 ocp_convert 的任务。注意到其实参 flags LOOKUP_NORMAL (比特集 LOOKUP_PROTECT | LOOKUP_COMPLAIN ),它强制该函数查找匹配的构造函数,但跳过转换操作符;而实参 convtype 则是比特集 CONV_IMPLICIT|CONV_FORCE_TEMP ,它表示查找隐式转换并需要 一个临时对象来保存结果。

对于其他非引用类型的函数形参,它遵循与赋值相同的规则,即该函数的实参可以被视作一个表达式右手边部分。

 

5712 static tree

5713 convert_for_assignment (tree type, tree rhs,                                                in typeck.c

5714                       const char *errtype, tree fndecl, int parmnum)

5715 {

5716    tree rhstype;

5717    enum tree_code coder;

5718

5719     /* Strip NON_LVALUE_EXPRs since we aren't using as an lvalue.  */

5720    if (TREE_CODE (rhs) == NON_LVALUE_EXPR)

5721      rhs = TREE_OPERAND (rhs, 0);

5722

5723    rhstype = TREE_TYPE (rhs);

5724    coder = TREE_CODE (rhstype);

5725

5726    if (TREE_CODE (type) == VECTOR_TYPE && coder == VECTOR_TYPE

5727       && ((*targetm .vector_opaque_p) (type)

5728            || (*targetm .vector_opaque_p) (rhstype)))

5729      return convert (type, rhs);

5730

5731    if (rhs == error_mark_node || rhstype == error_mark_node)

5732      return error_mark_node;

5733    if (TREE_CODE (rhs) == TREE_LIST && TREE_VALUE (rhs) == error_mark_node)

5734      return error_mark_node;

5735

5736    /* The RHS of an assignment cannot have void type.  */

5737    if (coder == VOID_TYPE)

5738    {

5739      error ("void value not ignored as it ought to be");

5740      return error_mark_node;

5741    }

5742

5743    /* Simplify the RHS if possible.  */

5744    if (TREE_CODE (rhs) == CONST_DECL)

5745      rhs = DECL_INITIAL (rhs);

5746   

5747    /* We do not use decl_constant_value here because of this case:

5748

5749       const char* const s = "s";

5750 

5751      The conversion rules for a string literal are more lax than for a

5752      variable; in particular, a string literal can be converted to a

5753      "char *" but the variable "s" cannot be converted in the same

5754      way. If the conversion is allowed, the optimization should be

5755      performed while creating the converted expression.  */

5756

5757    /* [expr.ass]

5758

5759      The expression is implicitly converted (clause _conv_) to the

5760      cv-unqualified type of the left operand.

5761

5762      We allow bad conversions here because by the time we get to this point

5763      we are committed to doing the conversion. If we end up doing a bad

5764      conversion, convert_like will complain.  */

5765    if (!can_convert_arg_bad (type, rhstype, rhs))

5766    {

5767      /* When -Wno-pmf-conversions is use, we just silently allow

5768        conversions from pointers-to-members to plain pointers. If

5769        the conversion doesn't work, cp_convert will complain.  */

5770      if (!warn_pmf2ptr

5771         && TYPE_PTR_P (type)

5772         && TYPE_PTRMEMFUNC_P (rhstype))

5773        rhs = cp_convert (strip_top_quals (type), rhs);

5774      else

5775      {

5776        /* If the right-hand side has unknown type, then it is an

5777          overloaded function. Call instantiate_type to get error

5778          messages.  */

5779        if (rhstype == unknown_type_node)

5780           instantiate_type (type, rhs, tf_error | tf_warning);

5781        else if (fndecl)

5782          error ("cannot convert `%T' to `%T' for argument `%P' to `%D'",

5783               rhstype, type, parmnum, fndecl);

5784        else

5785          error ("cannot convert `%T' to `%T' in %s", rhstype, type,

5786                errtype);

5787        return error_mark_node;

5788      }

5789    }

5790    return perform_implicit_conversion (strip_top_quals (type), rhs);

5791 }

 

上面的注释解释了在赋值中对转换的特殊处理。把实参转换到形参类型也适用隐式转换。下面的函数,类似于 can_convert_arg 查找转换序列,但允许可疑的转换。

 

6005 bool

6006 can_convert_arg_bad (tree to, tree from, tree arg)                                              in call.c

6007 {

6008    return implicit_conversion (to, from, arg, LOOKUP_NORMAL) != 0;

6009 }

 

在这里这个形参不是引用类型,那么被转换的实参,在其后的使用之前,应该先拷贝其值,因此这需要把实参转换到形参没有最顶端的 cv- 限定的类型。余下的处理与其他转换的方式相同。

回到 convert_arguments ,对于 2635 行的省略实参,对于这里非引用类型的形参,而引用类型的实参,该实参值就是我们所感兴趣的。在 2641 行,代码 BUILT_IN_CONSTANT_P 对应于内建函数“ constant_p ”,该函数接受可变实参,并显示其实参是否是常量。而看到在 2647 行的 convert_arg_to_ellipsis ,当该实参不是 POD 类型(这个类型可以被传给 constant_p ,这个函数仅评估其常量性而不关心其他事)时,给出警告。因此就不再需要费力调用 convert_arg_to_ellipsis

接着下面的代码处理缺省实参的情形,它与在隐式转换中查找用户定义转换的过程相同。

 

convert_arguments (continue)

 

2656    if (typetail != 0 && typetail != void_list_node)

2657     {

2658       /* See if there are default arguments that can be used.  */

2659      if (TREE_PURPOSE (typetail)

2660         && TREE_CODE (TREE_PURPOSE (typetail)) != DEFAULT_ARG)

2661      {

2662        for (; typetail != void_list_node; ++i)

2663        {

2664          tree parmval

2665                = convert_default_arg (TREE_VALUE (typetail),

2666                                   TREE_PURPOSE (typetail),

2667                                   fndecl, i);

2668

2669           if (parmval == error_mark_node)

2670            return error_mark_node;

2671

2672          result = tree_cons (0, parmval, result);

2673          typetail = TREE_CHAIN (typetail);

2674           /* ends with `...'.  */

2675          if (typetail == NULL_TREE)

2676            break ;

2677        }

2678      }

2679      else

2680      {

2681        if (fndecl)

2682        {

2683          cp_error_at ("too few arguments to %s `%+#D'",

2684                     called_thing, fndecl);

2685          error ("at this point in file");

2686        }

2687        else

2688          error ("too few arguments to function");

2689        return error_mark_list ;

2690      }

2691    }

2692

2693    return nreverse (result);

2694 }

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值