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

5.13.1.2.              真正的转换

在前一节,转换序列被确定;那么执行这个序列,可以从转换中得到目标类型。在 C++ 的前端中,在下面的 convert_like_real 里,转换将真正执行。在其参数中, fn argnum 用于诊断的目的,其中 argnum 是从 0 开始, -1 表示方法的‘ this ’指针实参。而 inner 是非 0 值,当被调用继续一个转换链时。当应用一个引用绑定时,它是负值;否则就是正值。如果 issue_conversion_warnings true ,合适的话,将对可疑的转换发出警告。

5.13.1.2.1.        扩展支持的不良标准转换

首先,如果转换序列是不良的( 3924 行的 ICS_BAD_FLAG 成立),并且是一个标准转换,为了尽可能给出正确的诊断信息,前端仍然会通过递归 convert_like_real 来执行转换序列中正确的部分。

 

3917 static tree

3918 convert_like_real (tree convs, tree expr, tree fn, int argnum, int inner,                   in call.c

3919                 bool issue_conversion_warnings)

3920 {

3921    tree totype = TREE_TYPE (convs);

3922    void (*diagnostic_fn)(const char *, ...);

3923

3924    if (ICS_BAD_FLAG (convs)

3925        && TREE_CODE (convs) != USER_CONV

3926        && TREE_CODE (convs) != AMBIG_CONV

3927        && TREE_CODE (convs) != REF_BIND)

3928    {

3929       tree t = convs;

3930      for (; t; t = TREE_OPERAND (t, 0))

3931      {

3932        if (TREE_CODE (t) == USER_CONV || !ICS_BAD_FLAG (t))

3933        {

3934          expr = convert_like_real (t, expr, fn, argnum, 1,

3935                               /*issue_conversion_warnings=*/ false);

3936          break ;

3937        }

3938        else if (TREE_CODE (t) == AMBIG_CONV)

3939          return convert_like_real (t, expr, fn, argnum, 1,

3940                              /*issue_conversion_warnings=*/ false);

3941        else if (TREE_CODE (t) == IDENTITY_CONV)

3942          break ;

3943      }

3944      pedwarn ("invalid conversion from `%T' to `%T'", TREE_TYPE (expr), totype);

3945      if (fn)

3946        pedwarn ("  initializing argument %P of `%D'", argnum, fn);

3947      return cp_convert (totype, expr);

3948    }

 

在这里这个不良转换不为标准所允许,但还是为扩展所接受的;否则在前一节就会给出 NULL ,而不是 convs 。扩展的行为只是忽略不良的部分,并发出警告。

5.13.1.2.2.        用户定义转换

在序列中可能有一部分是合法的但包含了潜在的危险性。如果 issue_conversion_warnings 是非 0 值,调用 dubious_conversion_warnings 来做这个检查。

 

5657 tree

5658 dubious_conversion_warnings (tree type, tree expr,                                      in typeck.c

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

5660 {

5661    type = non_reference (type);

5662   

5663    /* Issue warnings about peculiar, but valid, uses of NULL.  */

5664    if (ARITHMETIC_TYPE_P (type) && expr == null_node)

5665    {

5666      if (fndecl)

5667        warning ("passing NULL used for non-pointer %s %P of `%D'",

5668                errtype, parmnum, fndecl);

5669      else

5670        warning ("%s to non-pointer type `%T' from NULL", errtype, type);

5671    }

5672   

5673    /* Warn about assigning a floating-point type to an integer type.  */

5674    if (TREE_CODE (TREE_TYPE (expr)) == REAL_TYPE

5675        && TREE_CODE (type) == INTEGER_TYPE)

5676    {

5677      if (fndecl)

5678        warning ("passing `%T' for %s %P of `%D'",

5679                TREE_TYPE (expr), errtype, parmnum, fndecl);

5680      else

5681        warning ("%s to `%T' from `%T'", errtype, type, TREE_TYPE (expr));

5682    }

5683    /* And warn about assigning a negative value to an unsigned

5684      variable.  */

5685    else if (TREE_UNSIGNED (type) && TREE_CODE (type) != BOOLEAN_TYPE)

5686    {

5687      if (TREE_CODE (expr) == INTEGER_CST

5688         && TREE_NEGATED_INT (expr))

5689      {

5690        if (fndecl)

5691          warning ("passing negative value `%E' for %s %P of `%D'",

5692                   expr, errtype, parmnum, fndecl);

5693        else

5694          warning ("%s of negative value `%E' to `%T'",

5695                  errtype, expr, type);

5696      }

5697

5698      overflow_warning (expr);

5699

5700      if (TREE_CONSTANT (expr))

5701        expr = fold (expr);

5702    }

5703    return expr;

5704 }

 

上面对 expr 可能的改变是,如果 expr 是常量,对它执行的常量折叠( constant-folding )。在检查了有危险性的操作后,根据其类型开处理转换。对于用户定义转换序列,它由下面的代码处理。

 

convert_like_real (continue)

 

3950    if (issue_conversion_warnings)

3951      expr = dubious_conversion_warnings

3952                    (totype, expr, "converting", fn, argnum);

3953    switch (TREE_CODE (convs))

3954    {

3955      case USER_CONV:

3956      {

3957        struct z_candidate *cand = USER_CONV_CAND (convs);

3958        tree convfn = cand->fn;

3959        tree args;

3960

3961        if (DECL_CONSTRUCTOR_P (convfn))

3962        {

3963          tree t = build_int_2 (0, 0);

3964          TREE_TYPE (t) = build_pointer_type (DECL_CONTEXT (convfn));

3965

3966          args = build_tree_list (NULL_TREE, expr);

3967          if (DECL_HAS_IN_CHARGE_PARM_P (convfn)

3968             || DECL_HAS_VTT_PARM_P (convfn))

3969            /* We should never try to call the abstract or base constructor

3970               from here.  */

3971            abort ();

3972          args = tree_cons (NULL_TREE, t, args);

3973        }

3974        else

3975          args = build_this (expr);

3976        expr = build_over_call (cand, LOOKUP_NORMAL);

3977

3978        /* If this is a constructor or a function returning an aggr type,

3979          we need to build up a TARGET_EXPR.  */

3980        if (DECL_CONSTRUCTOR_P (convfn))

3981          expr = build_cplus_new (totype, expr);

3982

3983        /* The result of the call is then used to direct-initialize the object

3984          that is the destination of the copy-initialization. [dcl.init]

3985

3986          Note that this step is not reflected in the conversion sequence;

3987          it affects the semantics when we actually perform the

3988          conversion, but is not considered during overload resolution.

3989

3990          If the target is a class, that means call a ctor.  */

3991        if (IS_AGGR_TYPE (totype)

3992           && (inner >= 0 || !lvalue_p (expr)))

3993        {

3994          expr = (build_temp

3995                     (expr, totype,

3996                      /* Core issue 84, now a DR, says that we don't

3997                        allow UDCs for these args (which deliberately

3998                        breaks copy-init of an auto_ptr<Base> from an

3999                        auto_ptr<Derived>).  */

4000 LOOKUP_NORMAL|LOOKUP_ONLYCONVERTING|LOOKUP_NO_CONVERSION,

4001                      &diagnostic_fn));

4002           

4003          if (diagnostic_fn)

4004          {

4005            if (fn)

4006              diagnostic_fn

4007                  ("  initializing argument %P of `%D' from result of `%D'",

4008                    argnum, fn, convfn);

4009            else

4010              diagnostic_fn

4011                  ("  initializing temporary from result of `%D'",  convfn);

4012          }

4013          expr = build_cplus_new (totype, expr);

4014        }

4015        return expr;

4016      }

 

在前一节中,我们已经看到如果为用户定义转换找到了最佳候选者(以 z_candidate 的形式),一个 WRAPPER 节点被构建来封装找到的 z_candidate ,而它又被作为 convs 的第一个操作数,那么在 3957 行, USER_CONV_CAND 获取了这个 z_candidate 。因此在 3958 行的 convfn 指向对应的函数声明。

很可能构造函数被用于转换,在这个情况下,传入的“ this ”指针应该是 NULL ,而在退出时指向被构建的对象。在实参列表中准备了这个“ this ”指针之后, build_over_call 为调用这个重载构建所需的代码。

5.13.1.2.2.1.  转换隐含的 this 实参

在构建转换序列的过程中,如果压制了警告消息的发布,在 joust 中所有的警告信息都会被保存在 4392 行的 warnings 域里;因此如果 warnings 域不是空的,意味着有仍未了结的警告,现在是时候发布这些警告,因为我们就要执行真正的转换了。

4396 行, DECL_FUNCTION_MEMBER_P 成立,如果 fn 是应该成员函数(静态或非静态)。对于这个情形,需要通过“ cand->access_path ”——它保存了要访问这个成员函数的基类的 binfo ,来检查可访问性。

 

4362 static tree

4363 build_over_call (struct z_candidate *cand, int flags)                                            in call.c

4364 {

4365    tree fn = cand->fn;

4366    tree args = cand->args;

4367    tree convs = cand->convs;

4368    tree converted_args = NULL_TREE;

4369    tree parm = TYPE_ARG_TYPES (TREE_TYPE (fn));

4370    tree conv, arg, val;

4371    int i = 0;

4372    int is_method = 0;

4373

4374     /* In a template, there is no need to perform all of the work that

4375      is normally done. We are only interested in the type of the call

4376      expression, i.e., the return type of the function. Any semantic

4377      errors will be deferred until the template is instantiated.  */

4378    if (processing_template_decl )

4379    {

4380      tree expr;

4381      tree return_type;

4382      return_type = TREE_TYPE (TREE_TYPE (fn));

4383      expr = build (CALL_EXPR, return_type, fn, args);

4384      if (TREE_THIS_VOLATILE (fn) && cfun )

4385        current_function_returns_abnormally = 1;

4386      if (!VOID_TYPE_P (return_type))

4387        require_complete_type (return_type);

4388      return convert_from_reference (expr);

4389    }

4390

4391    /* Give any warnings we noticed during overload resolution.  */

4392    if (cand->warnings)

4393      for (val = cand->warnings; val; val = TREE_CHAIN (val))

4394        joust (cand, WRAPPER_ZC (TREE_VALUE (val)), 1);

4395

4396    if (DECL_FUNCTION_MEMBER_P (fn))

4397    {

4398       /* If FN is a template function, two cases must be considered.

4399        For example:

4400

4401         struct A {

4402         protected:

4403           template <class T> void f();

4404         };

4405         template <class T> struct B {

4406         protected:

4407           void g();

4408         };

4409         struct C : A, B<int> {

4410           using A::f;   // #1

4411           using B<int>::g;   // #2

4412         };

4413

4414        In case #1 where `A::f' is a member template, DECL_ACCESS is

4415        recorded in the primary template but not in its specialization.

4416        We check access of FN using its primary template.

4417

4418        In case #2, where `B<int>::g' has a DECL_TEMPLATE_INFO simply

4419        because it is a member of class template B, DECL_ACCESS is

4420        recorded in the specialization `B<int>::g'. We cannot use its

4421        primary template because `B<T>::g' and `B<int>::g' may have

4422         different access.  */

4423      if (DECL_TEMPLATE_INFO (fn)

4424         && is_member_template (DECL_TI_TEMPLATE (fn)))

4425        perform_or_defer_access_check (cand->access_path,

4426                                    DECL_TI_TEMPLATE (fn));

4427      else

4428        perform_or_defer_access_check (cand->access_path, fn);

4429    }

4430

4431    if (args && TREE_CODE (args) != TREE_LIST)

4432      args = build_tree_list (NULL_TREE, args);

4433    arg = args;

4434

4435    /* The implicit parameters to a constructor are not considered by overload

4436      resolution, and must be of the proper type.  */

4437    if (DECL_CONSTRUCTOR_P (fn))

4438    {

4439      converted_args = tree_cons (NULL_TREE, TREE_VALUE (arg), converted_args);

4440      arg = TREE_CHAIN (arg);

4441      parm = TREE_CHAIN (parm);

4442      if (DECL_HAS_IN_CHARGE_PARM_P (fn))

4443        /* We should never try to call the abstract constructor.  */

4444        abort ();

4445      if (DECL_HAS_VTT_PARM_P (fn))

4446      {

4447        converted_args = tree_cons

4448                       (NULL_TREE, TREE_VALUE (arg), converted_args);

4449        arg = TREE_CHAIN (arg);

4450        parm = TREE_CHAIN (parm);

4451      }

4452    }     

4453    /* Bypass access control for 'this' parameter.  */

4454    else if (TREE_CODE (TREE_TYPE (fn)) == METHOD_TYPE)

4455    {

4456      tree parmtype = TREE_VALUE (parm);

4457      tree argtype = TREE_TYPE (TREE_VALUE (arg));

4458      tree converted_arg;

4459      tree base_binfo;

4460       

4461      if (ICS_BAD_FLAG (TREE_VEC_ELT (convs, i)))

4462        pedwarn ("passing `%T' as `this' argument of `%#D' discards qualifiers",

4463                TREE_TYPE (argtype), fn);

4464

4465      /* [class.mfct.nonstatic]: If a nonstatic member function of a class

4466        X is called for an object that is not of type X, or of a type

4467        derived from X, the behavior is undefined.

4468

4469        So we can assume that anything passed as 'this' is non-null, and

4470        optimize accordingly.  */

4471      my_friendly_assert (TREE_CODE (parmtype) == POINTER_TYPE, 19990811);

4472      /* Convert to the base in which the function was declared.  */

4473      my_friendly_assert (cand->conversion_path != NULL_TREE, 20020730);

4474      converted_arg = build_base_path (PLUS_EXPR,

4475                                  TREE_VALUE (arg),

4476                                  cand->conversion_path,

4477                                  1);

4478      /* Check that the base class is accessible.  */

4479      if (!accessible_base_p (TREE_TYPE (argtype),

4480                         BINFO_TYPE (cand->conversion_path)))

4481         error ("`%T' is not an accessible base of `%T'",

4482               BINFO_TYPE (cand->conversion_path),

4483               TREE_TYPE (argtype));

4484      /* If fn was found by a using declaration, the conversion path

4485        will be to the derived class, not the base declaring fn. We

4486        must convert from derived to base.  */

4487      base_binfo = lookup_base (TREE_TYPE (TREE_TYPE (converted_arg)),

4488                             TREE_TYPE (parmtype), ba_ignore, NULL);

4489      converted_arg = build_base_path (PLUS_EXPR, converted_arg,

4490                                  base_binfo, 1);

4491       

4492      converted_args = tree_cons (NULL_TREE, converted_arg, converted_args);

4493      parm = TREE_CHAIN (parm);

4494      arg = TREE_CHAIN (arg);

4495      ++i;

4496      is_method = 1;

4497    }

 

上面在调用 build_over_call 之前,对于非构造函数,构建了一个与 expr 类型相同的“ this ”指针。不过,这个方法可能被定义在基类,而不是在 expr 的类型中,对于这个情形,需要从该类型获取这个基类。因此在 4475 行, arg 将是在 convert_like_real 中准备的“ this ”指针,而 build_base_path 产生 PLUS_EXPR 来调整 arg 指向由“ cand->conversion_path ”指定的基类的开头。

接着在 4487 行,正如我们已经看到的,“ TREE_TYPE (converted_arg) ”将是基类类型的 TYPE_DECL ,其 TREE_TYPE 将是对应的 RECORD_TYPE ;而 parmtype 是在方法定义中隐含的“ this ”参数,它将是真正定义这个方法的类。 4487 行开始,需要进一步的调整,出于由下面例子所显示的事实:

class A {

protected : int operator () { return 0; }

};

class B: public A {

public : using A::operator ;

};

class C: public B {};

void func (int) {}

int main() {

func (C c);

}

当查找转换序列时,前端将选择 B 作为“ cand->conversion_path ”;不过,该转换操作符事实上在 A 里定义。这个真正的“ this ”实参应该是派生类中的子对象 A 。注意,通过 using 指示( using directive ),基类的私有成员可以在派生类中可见,甚至于在外部的名字空间。因此 4487 行开始的调整,在 lookup_base 中检查可访问性就足够了。

5.13.1.2.2.2.  转换其他实参

5.12.5.2.2.2.1.          普通实参

在转换了隐含的“ this ”实参之后,接着转换其他实参。下面, arg 是用户所传递的实参列表,其中的节点 TREE_VALUE 是对应实参的表达式;而 parm 则是该函数的形参列表,其中的节点 TREE_VALUE 是对应的类型,而 TREE_PURPOSE 是实参缺省值,如果有的话。

 

build_over_call (continue)

 

4499    for (; arg && parm;

4500         parm = TREE_CHAIN (parm), arg = TREE_CHAIN (arg), ++i)

4501    {

4502      tree type = TREE_VALUE (parm);

4503

4504      conv = TREE_VEC_ELT (convs, i);

4505      val = convert_like_with_context

4506                (conv, TREE_VALUE (arg), fn, i - is_method);

4507

4508      val = convert_for_arg_passing (type, val);

4509      converted_args = tree_cons (NULL_TREE, val, converted_args);

4510    }

4511

4512    /* Default arguments */

4513    for (; parm && parm != void_list_node; parm = TREE_CHAIN (parm), i++)

4514      converted_args

4515        = tree_cons (NULL_TREE,

4516                  convert_default_arg (TREE_VALUE (parm),

4517                                   TREE_PURPOSE (parm),

4518                                   fn, i - is_method),

4519                  converted_args);

4520

4521    /* Ellipsis */

4522    for (; arg; arg = TREE_CHAIN (arg))

4523    {

4524      tree a = TREE_VALUE (arg);

4525      if (magic_varargs_p (fn))

4526        /* Do no conversions for magic varargs.  */ ;

4527       else

4528        a = convert_arg_to_ellipsis (a);

4529      converted_args = tree_cons (NULL_TREE, a, converted_args);

4530    }

4531

4532    converted_args = nreverse (converted_args);

 

另外, convs TREE_VEC_ELT 记录了对应于用户提供的实参的转换序列。连同这个转换序列,在 4508 行的 convert_like_with_context 被对应如下。

 

52      #define convert_like_with_context (CONV, EXPR, FN, ARGNO)    /                    in call.c

53        convert_like_real ((CONV), (EXPR), (FN), (ARGNO), 0,          /

54                         /*issue_conversion_warnings=*/ true)

 

它为每个实参调用了 convert_like_real 。这里警告信息应该被尽快地发布,并且对于实参,它是一个顶层的调用,因而实参 inner 的值是 0

在经过了我们现在正在学习的普通转换后,实参需要由下面函数提供的额外转换。

 

4317 tree

4318 convert_for_arg_passing (tree type, tree val)                                                      in call.c

4319 {

4320    if (val == error_mark_node)

4321      ;

4322     /* Pass classes with copy ctors by invisible reference.  */

4323    else if (TREE_ADDRESSABLE (type))

4324      val = build1 (ADDR_EXPR, build_reference_type (type), val);

4325    else if (PROMOTE_PROTOTYPES

4326          && INTEGRAL_TYPE_P (type)

4327          && COMPLETE_TYPE_P (type)

4328          && INT_CST_LT_UNSIGNED (TYPE_SIZE (type),

4329                                      TYPE_SIZE (integer_type_node)))

4330      val = perform_integral_promotions (val);

4331    return val;

4332 }

 

考虑以下的例子 [7]

A a;

func (a);   // of declaration void func (A a)

编译器将产生下面的伪代码来实现上述语句的语义:

A __temp0;      // temporary generated by compiler

__temp0.A::A(a);     // invocation of copy ctor by compiler

func (__temp0);

拷贝构造函数必须被调用来初始化临时对象,以符合拷贝构造函数的语义。如果编译器把 func 改变为“ void func (A& a) ”,效率会更高。这里有一个额外前提条件,就是“ a ”必须不能放入寄存器(对于放入寄存器这个情形, TREE_ADDRESSABLE 返回 0 )。

然后, 4325 行的 PROMOTE_PROTOTYPES ,对于 x86 芯片,被定义为 1 ,它表示当一个原型声明为‘ char ’或‘ short ’,真正传入一个‘ int ’( x86 向栈压入小于 int 的数据,相当不易)。因此在 4330 行的 perform_integral_promotions ,对这种情形,执行这个提升。

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值