Studying note of GCC-3.4.6 source (145)

5.13.   Stage after parsing

5.13.1.      Preliminaries - detail of conversion

5.13.1.1.              Determine appropriate conversion

C++ is a type-strong language. However, in practice, we always can write code like: “1 + 5.0f;”, in which “1” is of integer type, and “5.0f” is of floating point type. We can even write more amazing code like: “a + b;” assuming both “a” and “b” are instances of certain class. It dues to the compiler will generate code for us to do the necessary and appropriate conversions. In this section, we will see the conversions that compiler will use behind and rules of these conversions enacted by the language standard.

We first begin with function can_convert_arg , which is used to test if two specified types agree with each other.

 

5996   bool

5997   can_convert_arg (tree to, tree from, tree arg)                                                    in call.c

5998   {

5999     tree t = implicit_conversion (to, from, arg, LOOKUP_NORMAL);

6000     return (t && ! ICS_BAD_FLAG (t));

6001   }

 

Indicated by its name, conversions carried in below function, are those the compiler can be used freely as it considers as appropriately. It is one of interesting and powerful features of C++ language; and frequently confuses programmer even the seniors. In [3], section 13.3.3.1 “Implicit conversion sequences” explains what implicit conversion is in detail:

1.  An implicit conversion sequence is a sequence of conversions used to convert an argument in a function call to the type of the corresponding parameter of the function being called. The sequence of conversions is an implicit conversion as defined in clause 4, which means it is governed by the rules for initialization of an object or reference by a single expression (8.5, 8.5.3).

2. Implicit conversion sequences are concerned only with the type, cv-qualification, and lvalueness of the argument and how these are converted to match the corresponding properties of the parameter. Other properties, such as the lifetime, storage class, alignment, or accessibility of the argument and whether or not the argument is a bit-field are ignored. So, although an implicit conversion sequence can be defined for a given argument-parameter pair, the conversion from the argument to the parameter might still be ill-formed in the final analysis.

3.  A well-formed implicit conversion sequence is one of the following forms:

— a standard conversion sequence (13.3.3.1.1),

— a user-defined conversion sequence (13.3.3.1.2), or

— an ellipsis conversion sequence (13.3.3.1.3).

4.  However, when considering the argument of a user-defined conversion function that is a candidate by 13.3.1.3 when invoked for the copying of the temporary in the second step of a class copy-initialization, or by 13.3.1.4, 13.3.1.5, or 13.3.1.6 in all cases, only standard conversion sequences and ellipsis conversion sequences are allowed.

5.  For the case where the parameter type is a reference, see 13.3.3.1.4. (reference binding )

6.  When the parameter type is not a reference, the implicit conversion sequence models a copy-initialization of the parameter from the argument expression. The implicit conversion sequence is the one required to convert the argument expression to an rvalue of the type of the parameter. [Note: when the parameter has a class type, this is a conceptual conversion defined for the purposes of clause 13; the actual initialization is defined in terms of constructors and is not a conversion.] Any difference in top-level cv-qualification is subsumed by the initialization itself and does not constitute a conversion. [Example: a parameter of type A can be initialized from an argument of type const A. The implicit conversion sequence for that case is the identity sequence; it contains no “conversion” from const A to A.] When the parameter has a class type and the argument expression has the same type, the implicit conversion sequence is an identity conversion. When the parameter has a class type and the argument expression has a derived class type, the implicit conversion sequence is a derived-to-base Conversion from the derived class to the base class. [Note: there is no such standard conversion; this derived-to-base Conversion exists only in the description of implicit conversion sequences.] A derived-to-base Conversion has Conversion rank (13.3.3.1.1).

7.  In all contexts, when converting to the implicit object parameter or when converting to the left operand of an assignment operation only standard conversion sequences that create no temporary object for the result are allowed.

8.  If no conversions are required to match an argument to a parameter type, the implicit conversion sequence is the standard conversion sequence consisting of the identity conversion (13.3.3.1.1).

9.  If no sequence of conversions can be found to convert an argument to a parameter type or the conversion is otherwise ill-formed, an implicit conversion sequence cannot be formed.

10. If several different sequences of conversions exist that each convert the argument to the parameter type, the implicit conversion sequence associated with the parameter is defined to be the unique conversion sequence designated the ambiguous conversion sequence. For the purpose of ranking implicit conversion sequences as described in 13.3.3.2, the ambiguous conversion sequence is treated as a user-defined sequence that is indistinguishable from any other user-defined conversion sequence (The ambiguous conversion sequence is ranked with user-defined conversion sequences because multiple conversion sequences for an argument can exist only if they involve different user-defined conversions. The ambiguous conversion sequence is indistinguishable from any other user-defined conversion sequence because it represents at least two user-defined conversion sequences, each with a different user-defined conversion, and any other user-defined conversion sequence must be indistinguishable from at least one of them.

This rule prevents a function from becoming non-viable because of an ambiguous conversion sequence for one of its parameters. Consider this example,

class B;

class A { A (B&); };

class B { operator A (); };

class C { C (B&); };

void f(A) { }

void f(C) { }

B b;

f(b); //ambiguous because b -> C via constructor and

// b -> A via constructor or conversion function.

If it were not for this rule, f(A) would be eliminated as a viable function for the call f(b) causing overload resolution to select f(C) as the function to call even though it is not clearly the best choice. On the other hand, if an f(B) were to be declared then f(b) would resolve to that f(B) because the exact match with f(B) is better than any of the sequences required to match f(A).

If a function that uses the ambiguous conversion sequence is selected as the best viable function, the call will be ill-formed because the conversion of one of the arguments in the call is ambiguous.

11. The three forms of implicit conversion sequences mentioned above are defined in the following subclauses.

Before going ahead into the code, first we need clarify the concept of lvalue and rvalue. An lvalue refers to an object or function. It must be an entity that is addressable; i.e., if it refers to object, it should be able to be placed on left-hand-side of an assignment operation (that is why it called lvalue). And those that are non-lvalue are called rvalue (which can’t be used on left-hand-side of assginment operation).

 

1097 static tree

1098 implicit_conversion (tree to, tree from, tree expr, int flags)                                   in call.c

1099 {

1100    tree conv;

1101

1102    if (from == error_mark_node || to == error_mark_node

1103        || expr == error_mark_node)

1104      return NULL_TREE;

1105

1106    if (TREE_CODE (to) == REFERENCE_TYPE)

1107      conv = reference_binding (to, from, expr, flags);

1108    else

1109      conv = standard_conversion (to, from, expr, flags);

1110

1111    if (conv)

1112      return conv;

1113

1114    if (expr != NULL_TREE

1115       && (IS_AGGR_TYPE (from)

1116            || IS_AGGR_TYPE (to))

1117       && (flags & LOOKUP_NO_CONVERSION) == 0)

1118    {

1119      struct z_candidate *cand;

1120

1121      cand = build_user_type_conversion_1

1122              (to, expr, LOOKUP_ONLYCONVERTING);

1123      if (cand)

1124        conv = cand->second_conv;

1125

1126      /* We used to try to bind a reference to a temporary here, but that

1127        now handled by the recursive call to this function at the end

1128        of reference_binding.  */

1129      return conv;

1130    }

1131

1132    return NULL_TREE;

1133 }

 

See the conversion processing routines may recurse each other, here to handle reference type, the function invokes reference_binding again. However, as we are going forward within the tree of the expression, as long as no loop back of dependence exists there will not be the infinite recursion. And as C++ is the language requires declaration before using, mutual dependence is illegal; the parser should be able to find out this ill form.

5.13.1.1.1.        Case of standard conversion sequence

[3] section 13.3.3.1.1 “Standard conversion sequence” gives detail about the sequence.

1.  Table 9 summarizes the conversions defined in clause 4 (“Standard conversions”, section 4 in [3]) and partitions them into four disjoint categories: Lvalue Transformation, Qualification Adjustment, Promotion, and Conversion. [Note: these categories are orthogonal with respect to lvalue-ness, cv-qualification, and data representation: the Lvalue Transformations do not change the cv-qualification or data representation of the type; the Qualification Adjustments do not change the lvalue -ness or data representation of the type; and the Promotions and Conversions do not change the lvalue-ness or cv-qualification of the type.]

2.  [Note: As described in clause 4, a standard conversion sequence is either the Identity conversion by itself (that is, no conversion) or consists of one to three conversions from the other four categories. At most one conversion from each category is allowed in a single standard conversion sequence. If there are two or more conversions in the sequence, the conversions are applied in the canonical order: Lvalue Transformation , Promotion or Conversion , Qualification Adjustment . —end note ]

3.  Each conversion in Table 9 also has an associated rank (Exact Match, Promotion, or Conversion). These are used to rank standard conversion sequences (13.3.3.2). The rank of a conversion sequence is determined by considering the rank of each conversion in the sequence and the rank of any reference binding (13.3.3.1.4). If any of those has Conversion rank, the sequence has Conversion rank; otherwise, if any of those has Promotion rank, the sequence has Promotion rank; otherwise, the sequence has Exact Match rank.

Table 9: - conversions

Conversion

Category

Rank

Subclause

No conversions required

Identity

Exact Match

 

Lvalue-to-rvalue conversion

Lvalue Transformation

4.1

Array-to-pointer conversion

4.2

Function-to-pointer conversion

4.3

Qualification conversion

Qualification Adjustment

4.4

Integral promotions

Promotion

Promotion

4.5

Floating point promotion

4.6

Integral conversion

Conversion

Conversion

4.7

Floating point conversion

4.8

Floating-integral conversions

4.9

Pointer conversions

4.10

Pointer to member conversions

4.11

Boolean conversions

4.12

 

Below we just skip the resolving of overload from line 472 to 479 (it will be covered by section about reference binding, remember type_unknown_p returns true if expr is an overload). Always keep in mind that below standard_conversion handles implicit conversion, only conversions mentioned in above table are considered; never confuse it with force conversion.

 

456    static tree

457    standard_conversion (tree to, tree from, tree expr, int flags)                               in call.c

458    {

459      enum tree_code fcode, tcode;

460      tree conv;

461      bool fromref = false;

462   

463      to = v (to);

464      if (TREE_CODE (from) == REFERENCE_TYPE)

465      {

466        fromref = true;

467        from = TREE_TYPE (from);

468      }

469      to = strip_top_quals (to);

470      from = strip_top_quals (from);

471   

472      if ((TYPE_PTRFN_P (to) || TYPE_PTRMEMFUNC_P (to))

473         && expr && type_unknown_p (expr))

474      {

475        expr = instantiate_type (to, expr, tf_conv);

476        if (expr == error_mark_node)

477          return NULL_TREE;

478        from = TREE_TYPE (expr);

479      }

480   

481      fcode = TREE_CODE (from);

482      tcode = TREE_CODE (to);

483   

484      conv = build1 (IDENTITY_CONV, from, expr);

485   

486      if (fcode == FUNCTION_TYPE)

487      {

488        from = build_pointer_type (from);

489        fcode = TREE_CODE (from);

490        conv = build_conv (LVALUE_CONV, from, conv);

491      }

492      else if (fcode == ARRAY_TYPE)

493      {

494        from = build_pointer_type (TREE_TYPE (from));

495        fcode = TREE_CODE (from);

496        conv = build_conv (LVALUE_CONV, from, conv);

497      }

498      else if (fromref || (expr && lvalue_p (expr)))

499        conv = build_conv (RVALUE_CONV, from, conv);

500   

501      /* Allow conversion between `__complex__' data types.  */

502      if (tcode == COMPLEX_TYPE && fcode == COMPLEX_TYPE)

503      {

504        /* The standard conversion sequence to convert FROM to TO is

505          the standard conversion sequence to perform componentwise

506          conversion.  */

507        tree part_conv = standard_conversion

508            (TREE_TYPE (to), TREE_TYPE (from), NULL_TREE, flags);

509         

510        if (part_conv)

511         {

512          conv = build_conv (TREE_CODE (part_conv), to, conv);

513          ICS_STD_RANK (conv) = ICS_STD_RANK (part_conv);

514        }

515        else

516          conv = NULL_TREE;

517   

518        return conv;

519      }

520   

521      if (same_type_p (from, to))

522        return conv;

 

Line 484 builds IDENTITY_COV for identity conversion. The type of *_CONV node indicates the source type and its first operand is the expression to be converted. So conversion sequence will be built with a big nested *_CONV node, with the top *_CONV is the last conversion being done, and the innermost being the first which always is IDENTITY_CONV.

In internal, the front-end defines following ranks for conversions instead those shown by table-9 above. And note that the less value indicates more preference.

 

343    #define IDENTITY_RANK       0                                                                    in call.c

344    #define EXACT_RANK            1

345    #define PROMO_RANK           2

346    #define STD_RANK                 3

347    #define PBOOL_RANK             4

348    #define USER_RANK               5

349    #define ELLIPSIS_RANK         6

350    #define BAD_RANK                 7

 

Conversions that can be generated internally include: IDENTITY_CONV, LVALUE_CONV, QUAL_CONV, STD_CONV, PTR_CONV, PMEM_CONV, BASE_CONV, REF_BIND, USER_CONV, AMBIG_CONV, and RVALUE_CONV.

 

408    static tree

409    build_conv (enum tree_code code, tree type, tree from)                                      in call.c

410    {

411       tree t;

412      int rank = ICS_STD_RANK (from);

413   

414      /* We can't use buildl1 here because CODE could be USER_CONV, which

415        takes two arguments. In that case, the caller is responsible for

416        filling in the second argument.  */

417      t = make_node (code);

418      TREE_TYPE (t) = type;

419      TREE_OPERAND (t, 0) = from;

420   

421      switch (code)

422      {

423        case PTR_CONV:

424        case PMEM_CONV:

425        case BASE_CONV:

426        case STD_CONV:

427          if (rank < STD_RANK)

428            rank = STD_RANK;

429          break ;

430   

431        case QUAL_CONV:

432          if (rank < EXACT_RANK)

433            rank = EXACT_RANK;

434   

435        default :

436          break ;

437      }

438      ICS_STD_RANK (t) = rank;

439      ICS_USER_FLAG (t) = (code == USER_CONV || ICS_USER_FLAG (from));

440      ICS_BAD_FLAG (t) = ICS_BAD_FLAG (from);

441      return t;

442    }

 

In above function, ICS_STD_RANK in fact will store the highest rank value seen so far, whose usage we will see in later paragraphes.

Then above line 486 and 492 in standard_conversion handle function-to-pointer and array-to-point conversion, which is described by [3] clause 4.2 “Array-to-pointer conversion and clause” 4.3 “Function-to-pointer conversion” as below.

1.  An lvalue or rvalue of type “array ofN T” or “array of unknown bound of T” can be converted to an rvalue of type “pointer to T.” The result is a pointer to the first element of the array.

2.  A string literal (2.13.4) that is not a wide string literal can be converted to an rvalue of type “pointer to char”; a wide string literal can be converted to an rvalue of type “pointer to wchar_t”. In either case, the result is a pointer to the first element of the array. This conversion is considered only when there is an explicit appropriate pointer target type, and not when there is a general need to convert from an lvalue to an rvalue . [Note: this conversion is deprecated. See Annex D.] For the purpose of ranking in overload resolution (13.3.3.1.1), this conversion is considered an array-to-pointer conversion followed by a qualification conversion (4.4). [Example: "abc" is converted to “pointer to const char” as an array-to-pointer conversion, and then to “pointer to char” as a qualification conversion.]

1.  An lvalue of function type T can be converted to an rvalue of type “pointer to T.” The result is a pointer to the function. (This conversion never applies to nonstatic member functions because an lvalue that refers to a nonstatic member function cannot be obtained.)

2.  [Note: See 13.4 for additional rules for the case where the function is overloaded.]

Note that in terms 1 of both clauses, rvalue of type “pointer to T” just means the pointer itself can’t be modified, but its content can be changed. For example:

char a[10];

char *p = …;

a = p;              // a is converted to rvalue of char*, assignment not allowed

*(a+1) = *p;    // object pointed by a+1 is lvalue, it is OK

Compared with below clause, these two clauses include creation of pointer type, the front-end must tells out these two situations from other cases of lvalue transformation. So the front-end uses LVALUE_CONV for them.

Then in standard_conversion , arriving at line 498, if the condition is satisified, terms 1 mentioned in [3] clause 4.1 “Lvalue-to-rvalue conversion” will be implemented. The front-end uses RVALUE_CONV for the case. One difference of LVALUE_CONV and RVALUE_CONV is that when building LVALUE_CONV, we have known the rvalue (see the pointer type built at line 488 and 494), while for RVALUE_CONV, the rvalue is unknown at that point, and later the front-end needs invoke decay_conversion to do the real conversion.

1.  An lvalue (3.10) of a non-function, non-array type T can be converted to an rvalue . If T is an incomplete type, a program that necessitates this conversion is ill-formed. If the object to which the lvalue refers is not an object of type T and is not an object of a type derived from T, or if the object is uninitialized, a program that necessitates this conversion has undefined behavior. If T is a non-class type, the type of the rvalue is the cv-unqualified version of T. Otherwise, the type of the rvalue is T. [In C++ class rvalues can have cv-qualified types (because they are objects). This differs from ISO C, in which non-lvalues never have cv-qualified types.]

2.  The value contained in the object indicated by the lvalue is the rvalue result. When an lvalue-to-rvalue conversion occurs within the operand of sizeof (5.3.3) the value contained in the referenced object is not accessed, since that operator does not evaluate its operand.

Focus upon following function, given expression ref , it can tell if it is lvalue or not. And see that pointer type built for LVALUE_CONV above is rvalue told by this function. This function should be studied carefully.

 

211     int

212    lvalue_p (tree ref)                                                                                           in tree.c

213    {

214      return

215        (lvalue_p_1 (ref, /*class rvalue ok*/ 1) != clk_none);

216    }

 

The various kinds of lvalues we distinguish are given in below.

 

2964 typedef enum cp_lvalue_kind {                                                                   in cp-tree.h

2965    clk_none = 0,     /* Things that are not an lvalue.  */

2966    clk_ordinary = 1, /* An ordinary lvalue.  */

2967    clk_class = 2,    /* An rvalue of class-type.  */

2968    clk_bitfield = 4, /* An lvalue for a bit-field.  */

2969    clk_packed = 8    /* An lvalue for a packed field.  */

2970 } cp_lvalue_kind ;

 

In [3], clause 3.10 (lvalues and rvalues), gives the following explaination.

1.  Every expression is either an lvalue or an rvalue .

2.  An lvalue refers to an object or function. Some rvalue expressions—those of class or cv-qualified class type—also refer to objects (Expressions such as invocations of constructors and of functions that return a class type refer to objects, and the implementation can invoke a member function upon such objects, but the expressions are not lvalues ).

3.  [Note: some built-in operators and function calls yield lvalues . [Example: if E is an expression of pointer type, then *E is an lvalue expression referring to the object or function to which E points. As another example, the function

int& f();

yields an lvalue , so the call f() is an lvalue expression.]]

4.  [Note: some builtin operators expect lvalue operands. [Example: built-in assignment operators all expect their left hand operands to be lvalues .] Other built-in operators yield rvalues , and some expect them. [Example: the unary and binary + operators expect rvalue arguments and yield rvalue results.] The discussion of each built-in operator in clause 5 indicates whether it expects lvalue operands and whether it yields an lvalue .]

5.  The result of calling a function that does not return a reference is an rvalue . User defined operators are functions, and whether such operators expect or yield lvalues is determined by their parameter and return types.

6.  An expression which holds a temporary object resulting from a cast to a nonreference type is an rvalue (this includes the explicit creation of an object using functional notation (5.2.3)).

7.  Whenever an lvalue appears in a context where an rvalue is expected, the lvalue is converted to an rvalue ; see 4.1, 4.2, and 4.3.

8.  The discussion of reference initialization in 8.5.3 and of temporaries in 12.2 indicates the behavior of lvalues and rvalues in other significant contexts.

9.  Class rvalues can have cv-qualified types; non-class rvalues always have cv-unqualified types. Rvalues shall always have complete types or the void type; in addition to these types, lvalues can also have incomplete types.

10. An lvalue for an object is necessary in order to modify the object except that an rvalue of class type can also be used to modify its referent under certain circumstances. [Example: a member function called for an object (9.3) can modify the object.]

11. Functions cannot be modified, but pointers to functions can be modifiable.

12. A pointer to an incomplete type can be modifiable. At some point in the program when the pointed to type is complete, the object at which the pointer points can also be modified.

13. The referent of a const-qualified expression shall not be modified (through that expression), except that if it is of class type and has a mutable component, that component can be modified (7.1.5.1).

14. If an expression can be used to modify the object to which it refers, the expression is called modifiable. A program that attempts to modify an object through a nonmodifiable lvalue or rvalue expression is illformed.

15. If a program attempts to access the stored value of an object through an lvalue of other than one of the following types the behavior is undefined (The intent of this list is to specify those circumstances in which an object may or may not be aliased):

— the dynamic type of the object,

— a cv-qualified version of the dynamic type of the object,

— a type that is the signed or unsigned type corresponding to the dynamic type of the object,

— a type that is the signed or unsigned type corresponding to a cv-qualified version of the dynamic type of the object,

— an aggregate or union type that includes one of the aforementioned types among its members (including, recursively, a member of a subaggregate or contained union),

— a type that is a (possibly cv-qualified) base class type of the dynamic type of the object,

— a char or unsigned char type.

Considering non-static method processed below, as non-static method must be invoked as: “a.fa();” with given object which may not addressable (for example, in statement “A().fa();”, “A()” returns an rvalue), non-static method is just considered as rvalue. Further, expression “A().fa();” can modify the object, so “A()” can be considered as lvalue if not require strict lvalue (argument treat_class_rvalues_as_lvalues is 1). Pay attention to TARGET_EXPR at line 156, it would wrapsthe temperary object returned by “A()” ( see comment at line 161).

 

62      static cp_lvalue_kind

63      lvalue_p_1 (tree ref,                                                                                       in tree.c

64                int treat_class_rvalues_as_lvalues)

65      {

66        cp_lvalue_kind op1_lvalue_kind = clk_none;

67        cp_lvalue_kind op2_lvalue_kind = clk_none;

68     

69        if (TREE_CODE (TREE_TYPE (ref)) == REFERENCE_TYPE)

70          return clk_ordinary;

71     

72        if (ref == current_class_ptr )

73          return clk_none;

74     

75        switch (TREE_CODE (ref))

76        {

77           /* preincrements and predecrements are valid lvals, provided

78            what they refer to are valid lvals.  */

79          case PREINCREMENT_EXPR:

80          case PREDECREMENT_EXPR:

81          case SAVE_EXPR:

82          case UNSAVE_EXPR:

83          case TRY_CATCH_EXPR:

84          case WITH_CLEANUP_EXPR:

85          case REALPART_EXPR:

86          case IMAGPART_EXPR:

87            return lvalue_p_1 (TREE_OPERAND (ref, 0),

88                            treat_class_rvalues_as_lvalues);

89     

90          case COMPONENT_REF:

91            op1_lvalue_kind = lvalue_p_1 (TREE_OPERAND (ref, 0),

92                                       treat_class_rvalues_as_lvalues);

93            /* In an expression of the form "X.Y", the packed-ness of the

94              expression does not depend on "X".  */

95            op1_lvalue_kind &= ~clk_packed;

96             /* Look at the member designator.  */

97            if (!op1_lvalue_kind

98                /* The "field" can be a FUNCTION_DECL or an OVERLOAD in some     

99                  situations.  */

100              || TREE_CODE (TREE_OPERAND (ref, 1)) != FIELD_DECL)

101            ;

102          else if (DECL_C_BIT_FIELD (TREE_OPERAND (ref, 1)))

103          {

104            /* Clear the ordinary bit. If this object was a class

105              rvalue we want to preserve that information.  */

106            op1_lvalue_kind &= ~clk_ordinary;

107            /* The lvalue is for a bitfield.  */

108            op1_lvalue_kind |= clk_bitfield;

109          }

110           else if (DECL_PACKED (TREE_OPERAND (ref, 1)))

111             op1_lvalue_kind |= clk_packed;

112          

113           return op1_lvalue_kind;

114    

115         case STRING_CST:

116           return clk_ordinary;

117    

118         case VAR_DECL:

119           if (TREE_READONLY (ref) && ! TREE_STATIC (ref)

120             && DECL_LANG_SPECIFIC (ref)

121             && DECL_IN_AGGR_P (ref))

122            return clk_none;

123        case INDIRECT_REF:

124        case ARRAY_REF:

125        case PARM_DECL:

126        case RESULT_DECL:

127          if (TREE_CODE (TREE_TYPE (ref)) != METHOD_TYPE)

128            return clk_ordinary;

129          break ;

130   

131         /* A currently unresolved scope ref.  */

132        case SCOPE_REF:

133          abort ();

134        case MAX_EXPR:

135        case MIN_EXPR:

136          op1_lvalue_kind = lvalue_p_1 (TREE_OPERAND (ref, 0),

137                                    treat_class_rvalues_as_lvalues);

138          op2_lvalue_kind = lvalue_p_1 (TREE_OPERAND (ref, 1),

139                                     treat_class_rvalues_as_lvalues);

140          break ;

141   

142        case COND_EXPR:

143          op1_lvalue_kind = lvalue_p_1 (TREE_OPERAND (ref, 1),

144                                    treat_class_rvalues_as_lvalues);

145          op2_lvalue_kind = lvalue_p_1 (TREE_OPERAND (ref, 2),

146                                    treat_class_rvalues_as_lvalues);

147          break ;

148   

149        case MODIFY_EXPR:

150          return clk_ordinary;

151   

152        case COMPOUND_EXPR:

153          return lvalue_p_1 (TREE_OPERAND (ref, 1),

154                          treat_class_rvalues_as_lvalues);

155   

156        case TARGET_EXPR:

157          return treat_class_rvalues_as_lvalues ? clk_class : clk_none;

158   

159        case CALL_EXPR:

160        case VA_ARG_EXPR:

161          /* Any class-valued call would be wrapped in a TARGET_EXPR.  */

162          return clk_none;

163   

164        case FUNCTION_DECL:

165          /* All functions (except non-static-member functions) are

166            lvalues.  */

167          return (DECL_NONSTATIC_MEMBER_FUNCTION_P (ref)

168                ? clk_none : clk_ordinary);

169   

170        case NON_DEPENDENT_EXPR:

171          /* We must consider NON_DEPENDENT_EXPRs to be lvalues so that

172            things like "&E" where "E" is an expression with a

173            non-dependent type work. It is safe to be lenient because an

174            error will be issued when the template is instantiated if "E"

175            is not an lvalue.  */

176          return clk_ordinary;

177   

178        default :

179          break ;

180      }

181   

182      /* If one operand is not an lvalue at all, then this expression is

183        not an lvalue.  */

184      if (!op1_lvalue_kind || !op2_lvalue_kind)

185        return clk_none;

186   

187      /* Otherwise, it's an lvalue, and it has all the odd properties

188        contributed by either operand.  */

189      op1_lvalue_kind = op1_lvalue_kind | op2_lvalue_kind;

190      /* It's not an ordinary lvalue if it involves either a bit-field or

191        a class rvalue.  */

192      if ((op1_lvalue_kind & ~clk_ordinary) != clk_none)

193        op1_lvalue_kind &= ~clk_ordinary;

194      return op1_lvalue_kind;

195    }

 

Above, NON_DEPENDENT_EXPR is a placeholder for an expression that is not type-dependent, but does occur in a template. See INDIRECT_REF is lvalue (however pointer is rvalue itself, it is the case described by terms 3 above); and normal variable is also lvalue (line 118); further if an array is referred, its reference is lvalue, otherwise the array would be decayed to pointer of rvalue. Next note that MODIFY_EXPR at line 149, no doubt must be lvalue, and COMPOUND_EXPR following depends on its second operand. And for expressions may contains operand of lvalue, lvalue_p_1 recurs into the operand.

It is worth note that call expression “f();” of “int& f();” is lvalue, but expression like “f() = 5;” is illegal; the only case this lvalue can be used directly is using “f()” as parameter which is covered by PARM_DECL at line 125; so CALL_EXPR not within PARM_DECL is just regarded as rvalue. Further, being lvalue, PARM_DECL, RESULT_DECL, ARRAY_REF, and INDIRECT_REF can be of type of non-static method of class.

 

standard_conversion (continue)

 

524      if ((tcode == POINTER_TYPE || TYPE_PTR_TO_MEMBER_P (to))

525          && expr && null_ptr_cst_p (expr))

526        conv = build_conv (STD_CONV, to, conv);

527      else if (tcode == POINTER_TYPE && fcode == POINTER_TYPE

528            && TREE_CODE (TREE_TYPE (to)) == VECTOR_TYPE

529            && TREE_CODE (TREE_TYPE (from)) == VECTOR_TYPE

530            && ((*targetm .vector_opaque_p) (TREE_TYPE (to))

531                   || (*targetm .vector_opaque_p) (TREE_TYPE (from))))

532        conv = build_conv (STD_CONV, to, conv);

533      else if ((tcode == INTEGER_TYPE && fcode == POINTER_TYPE)

534             || (tcode == POINTER_TYPE && fcode == INTEGER_TYPE))

535      {

536        /* For backwards brain damage compatibility, allow interconversion of

537          pointers and integers with a pedwarn.  */

538        conv = build_conv (STD_CONV, to, conv);

539        ICS_BAD_FLAG (conv) = 1;

540      }

541      else if (tcode == ENUMERAL_TYPE && fcode == INTEGER_TYPE)

542      {

543        /* For backwards brain damage compatibility, allow interconversion of

544          enums and integers with a pedwarn.  */

545        conv = build_conv (STD_CONV, to, conv);

546        ICS_BAD_FLAG (conv) = 1;

547      }

 

Note above, converting between pointers and integers and between integers and enums are both bad conversions in the context of C++ langauge (but it just gives warning under C). So the conversion is recorded and given the rank of lowest priority.

 

standard_conversion (continue)

 

548      else if ((tcode == POINTER_TYPE && fcode == POINTER_TYPE)

549            || (TYPE_PTRMEM_P (to) && TYPE_PTRMEM_P (from)))

550      {

551        tree to_pointee;

552        tree from_pointee;

553   

554        if (tcode == POINTER_TYPE

555          && same_type_ignoring_top_level_qualifiers_p (TREE_TYPE (from),

556                                                  TREE_TYPE (to)))

557          ;

558        else if (VOID_TYPE_P (TREE_TYPE (to))

559               && !TYPE_PTRMEM_P (from)

560               && TREE_CODE (TREE_TYPE (from)) != FUNCTION_TYPE)

561        {

562          from = build_pointer_type

563                 (cp_build_qualified_type (void_type_node,

564                                       cp_type_quals (TREE_TYPE (from))));

565          conv = build_conv (PTR_CONV, from, conv);

566        }

567        else if (TYPE_PTRMEM_P (from))

568        {

569          tree fbase = TYPE_PTRMEM_CLASS_TYPE (from);

570          tree tbase = TYPE_PTRMEM_CLASS_TYPE (to);

571   

572          if (DERIVED_FROM_P (fbase, tbase)

573             && (same_type_ignoring_top_level_qualifiers_p

574                        (TYPE_PTRMEM_POINTED_TO_TYPE (from),

575                          TYPE_PTRMEM_POINTED_TO_TYPE (to))))

576          {

577            from = build_ptrmem_type (tbase,

578                           TYPE_PTRMEM_POINTED_TO_TYPE (from));

579            conv = build_conv (PMEM_CONV, from, conv);

580          }

581          else if (!same_type_p (fbase, tbase))

582            return NULL;

583        }

584        else if (IS_AGGR_TYPE (TREE_TYPE (from))

585               && IS_AGGR_TYPE (TREE_TYPE (to))

586          /* [conv.ptr]

587                 

588            An rvalue of type "pointer to cv D," where D is a

589            class type, can be converted to an rvalue of type

590            "pointer to cv B," where B is a base class (clause

591            _class.derived_) of D. If B is an inaccessible

592            (clause _class.access_) or ambiguous

593            (_class.member.lookup_) base class of D, a program

594            that necessitates this conversion is ill-formed.  */

595          /* Therefore, we use DERIVED_FROM_P, and not

596            ACESSIBLY_UNIQUELY_DERIVED_FROM_P, in this test.  */

597               && DERIVED_FROM_P (TREE_TYPE (to), TREE_TYPE (from)))

598        {

599          from =

600              cp_build_qualified_type (TREE_TYPE (to),

601                                   cp_type_quals (TREE_TYPE (from)));

602          from = build_pointer_type (from);

603          conv = build_conv (PTR_CONV, from, conv);

604        }

605   

606        if (tcode == POINTER_TYPE)

607        {

608          to_pointee = TREE_TYPE (to);

609          from_pointee = TREE_TYPE (from);

610        }

611         else

612        {

613          to_pointee = TYPE_PTRMEM_POINTED_TO_TYPE (to);

614          from_pointee = TYPE_PTRMEM_POINTED_TO_TYPE (from);

615        }

616   

617        if (same_type_p (from, to))

618          /* OK */ ;

619         else if (comp_ptr_ttypes (to_pointee, from_pointee))

620          conv = build_conv (QUAL_CONV, to, conv);

621        else if (expr && string_conv_p (to, expr, 0))

622          /* converting from string constant to char *.  */

623          conv = build_conv (QUAL_CONV, to, conv);

624        else if (ptr_reasonably_similar (to_pointee, from_pointee))

625        {

626          conv = build_conv (PTR_CONV, to, conv);

627          ICS_BAD_FLAG (conv) = 1;

628        }

629        else

630          return 0;

631   

632        from = to;

633      }

 

Again in [3] clause 4.10 “Pointer covnersions”, finds following paragraphes. And note that code at line 499 in the function has converted lvalue into rvalue, conv above holds the conversion.

1.  A null pointer constant is an integral constant expression (5.19) rvalue of integer type that evaluates to zero. A null pointer constant can be converted to a pointer type; the result is the null pointer value of that type and is distinguishable from every other value of pointer to object or pointer to function type. Two null pointer values of the same type shall compare equal. The conversion of a null pointer constant to a pointer to cv-qualified type is a single conversion, and not the sequence of a pointer conversion followed by a qualification conversion (4.4).

2.  An rvalue of type “pointer to cv T,” where T is an object type, can be converted to an rvalue of type “pointer to cv void.” The result of converting a “pointer to cv T” to a “pointer to cv void” points to the start of the storage location where the object of type T resides, as if the object is a most derived object (1.8) of type T (that is, not a base class subobject).

3.  An rvalue of type “pointer to cv D,” where D is a class type, can be converted to an rvalue of type “pointer to cv B,” where B is a base class (clause 10) of D. If B is an inaccessible (clause 11) or ambiguous (10.2) base class of D, a program that necessitates this conversion is ill-formed. The result of the conversion is a pointer to the base class sub-object of the derived class object. The null pointer value is converted to the null pointer value of the destination type.

Terms 1 is worked by line 526 above. It is one step STD_CONV. And condition of terms 2 is tested by code at line 558 ~ 560. Then condition in line 584 ~ 597 fullfils terms 3.

Further clause 4.11 “Pointer to member conversion” is shown in below.

1.  A null pointer constant (4.10) can be converted to a pointer to member type; the result is the null member pointer value of that type and is distinguishable from any pointer to member not created from a null pointer constant. Two null member pointer values of the same type shall compare equal. The conversion of a null pointer constant to a pointer to member of cv-qualified type is a single conversion, and not the sequence of a pointer to member conversion followed by a qualification conversion (4.4).

2.  An rvalue of type “pointer to member of B of type cv T,” where B is a class type, can be converted to an rvalue of type “pointer to member of D of type cv T,” where D is a derived class (clause 10) of B. If B is an inaccessible (clause 11), ambiguous (10.2) or virtual (10.1) base class of D, a program that necessitates this conversion is ill-formed. The result of the conversion refers to the same member as the pointer to member before the conversion took place, but it refers to the base class member as if it were a member of the derived class. The result refers to the member in D’s instance of B. Since the result has type “pointer to member of D of type cv T,” it can be dereferenced with a D object. The result is the same as if the pointer to member of B were dereferenced with the B sub-object of D. The null member pointer value is converted to the null member pointer value of the destination type. [The rule for conversion of pointers to members (from pointer to member of base to pointer to member of derived) appears inverted compared to the rule for pointers to objects (from pointer to derived to pointer to base) (4.10, clause 10). This inversion is necessary to ensure type safety. Note that a pointer to member is not a pointer to object or a pointer to function and the rules for conversions of such pointers do not apply to pointers to members. In particular, a pointer to member cannot be converted to a void*.]

Terms 1 is also covered by line 526 above. And terms 2 is handled by block at line 567. Arriving at line 617, with above conversion sequence, from and to should be either of same type, or just with different cv-qualifiers; otherwise, no conversion sequence is applicable.

Below conversion between pointer-to-method is similar with that of pointer-to-member. Note that fromfn and tofn are functions pointed respectively; fbase and tbase are the implicit this pointer each. So conversion can be carried out is: “an rvalue of type “pointer to method of B of type cv T,” where B is a class type, can be converted to an rvalue of type “pointer to the same method of D of type cv T,” where D is a derived class of B.” And see that “same” here is a little stricter than the that meaning among overloads.

 

standard_conversion (continue)

 

634      else if (TYPE_PTRMEMFUNC_P (to) && TYPE_PTRMEMFUNC_P (from))

635      {

636        tree fromfn = TREE_TYPE (TYPE_PTRMEMFUNC_FN_TYPE (from));

637        tree tofn = TREE_TYPE (TYPE_PTRMEMFUNC_FN_TYPE (to));

638        tree fbase = TREE_TYPE (TREE_VALUE (TYPE_ARG_TYPES (fromfn)));

639        tree tbase = TREE_TYPE (TREE_VALUE (TYPE_ARG_TYPES (tofn)));

640   

641        if (!DERIVED_FROM_P (fbase, tbase)

642           || !same_type_p (TREE_TYPE (fromfn), TREE_TYPE (tofn))

643           || !compparms (TREE_CHAIN (TYPE_ARG_TYPES (fromfn)),

644                        TREE_CHAIN (TYPE_ARG_TYPES (tofn)))

645           || cp_type_quals (fbase) != cp_type_quals (tbase))

646          return 0;

647   

648        from = cp_build_qualified_type (tbase, cp_type_quals (fbase));

649        from = build_method_type_directly (from,

650                                     TREE_TYPE (fromfn),

651                                     TREE_CHAIN (TYPE_ARG_TYPES (fromfn)));

652        from = build_ptrmemfunc_type (build_pointer_type (from));

653        conv = build_conv (PMEM_CONV, from, conv);

654    }

 

[3], clause 4.12 “Boolean conversions” gives guideline how to do the conversion as below.

1.  An rvalue of arithmetic, enumeration, pointer, or pointer to member type can be converted to an rvalue of type bool. A zero value, null pointer value, or null member pointer value is converted to false; any other value is converted to true.

However, conversion from pointer to boolean is alittle bit inferior to those of integer types.

 

standard_conversion (continue)

 

655      else if (tcode == BOOLEAN_TYPE)

656      {

657         /* [conv.bool]

658   

659          An rvalue of arithmetic, enumeration, pointer, or pointer to

660          member type can be converted to an rvalue of type bool.  */

661        if (ARITHMETIC_TYPE_P (from)

662           || fcode == ENUMERAL_TYPE

663           || fcode == POINTER_TYPE

664           || TYPE_PTR_TO_MEMBER_P (from))

665        {

666          conv = build_conv (STD_CONV, to, conv);

667          if (fcode == POINTER_TYPE

668             || TYPE_PTRMEM_P (from)

669             || (TYPE_PTRMEMFUNC_P (from)

670               && ICS_STD_RANK (conv) < PBOOL_RANK))

671            ICS_STD_RANK (conv) = PBOOL_RANK;

672          return conv;

673        }

674         

675        return NULL_TREE;

676      }

 

Then below code handles coversions described by [3], clause 4.5 “Integeral promotions”, 4.6 “Floating point promotion”, 4.7 “Integral conversions”, 4.8 “Floating point conversions”, and 4.9 “Floating-integral conversions”. And below paragraphes give the detail.

1.  An rvalue of type char , signed char , unsigned char , short int , or unsigned short int can be converted to an rvalue of type int if int can represent all the values of the source type; otherwise, the source rvalue can be converted to an rvalue of type unsigned int .

2.  An rvalue of type wchar_t (3.9.1) or an enumeration type (7.2) can be converted to an rvalue of the first of the following types that can represent all the values of its underlying type: int , unsigned int , long , or unsigned long .

3.  An rvalue for an integral bit-field (9.6) can be converted to an rvalue of type int if int can represent all the values of the bit-field; otherwise, it can be converted to unsigned int if unsigned int can represent all the values of the bit-field. If the bit-field is larger yet, no integral promotion applies to it. If the bit-field has an enumerated type, it is treated as any other value of that type for promotion purposes.

4.  An rvalue of type bool can be converted to an rvalue of type int , with false becoming zero and true becoming one.

5.  These conversions are called integral promotions.

1.  An rvalue of type float can be converted to an rvalue of type double . The value is unchanged.

2.  This conversion is called floating point promotion.

1.  An rvalue of an integer type can be converted to an rvalue of another integer type. An rvalue of an enumeration type can be converted to an rvalue of an integer type.

2.  If the destination type is unsigned, the resulting value is the least unsigned integer congruent to the source integer (modulo 2n where n is the number of bits used to represent the unsigned type). [Note: In a two’s complement representation, this conversion is conceptual and there is no change in the bit pattern (if there is no truncation).]

3.  If the destination type is signed, the value is unchanged if it can be represented in the destination type (and bit-field width); otherwise, the value is implementation-defined.

4.  If the destination type is bool , see 4.12. If the source type is bool , the value false is converted to zero and the value true is converted to one.

5.  The conversions allowed as integral promotions are excluded from the set of integral conversions.

1.  An rvalue of floating point type can be converted to an rvalue of another floating point type.  If the source value can be exactly represented in the destination type, the result of the conversion is that exact representation. If the source value is between two adjacent destination values, the result of the conversion is an implementation-defined choice of either of those values. Otherwise, the behavior is undefined.

2.  The conversions allowed as floating point promotions are excluded from the set of floating point conversions.

1.  An rvalue of a floating point type can be converted to an rvalue of an integer type. The conversion truncates; that is, the fractional part is discarded. The behavior is undefined if the truncated value cannot be represented in the destination type. [Note:If the destination type is bool, see 4.12.]

2.  An rvalue of an integer type or of an enumeration type can be converted to an rvalue of a floating point type. The result is exact if possible. Otherwise, it is an implementation-defined choice of either the next lower or higher representable value. [Note: loss of precision occurs if the integral value cannot be represented exactly as a value of the floating type.] If the source type is bool, the value false is converted to zero and the value true is converted to one.

Code from line 679 to 690 covers all cases mentioned above. As PROMO_RANK is preferred to STD_RANK, it always uses PROMO_RANK as possible.

 

standard_conversion (continue)

 

677      /* We don't check for ENUMERAL_TYPE here because there are no standard

678        conversions to enum type.  */

679      else if (tcode == INTEGER_TYPE || tcode == BOOLEAN_TYPE

680            || tcode == REAL_TYPE)

681      {

682        if (! (INTEGRAL_CODE_P (fcode) || fcode == REAL_TYPE))

683          return 0;

684        conv = build_conv (STD_CONV, to, conv);

685   

686        /* Give this a better rank if it's a promotion.  */

687        if (same_type_p (to, type_promotes_to (from))

688           && ICS_STD_RANK (TREE_OPERAND (conv, 0)) <= PROMO_RANK)

689          ICS_STD_RANK (conv) = PROMO_RANK;

690      }

691      else if (fcode == VECTOR_TYPE && tcode == VECTOR_TYPE

692            && ((*targetm .vector_opaque_p) (from)

693                 || (*targetm .vector_opaque_p) (to)))

694        return build_conv (STD_CONV, to, conv);

695      else if (!(flags & LOOKUP_CONSTRUCTOR_CALLABLE)

696             && IS_AGGR_TYPE (to) && IS_AGGR_TYPE (from)

697             && is_properly_derived_from (from, to))

698      {

699        if (TREE_CODE (conv) == RVALUE_CONV)

700          conv = TREE_OPERAND (conv, 0);

701        conv = build_conv (BASE_CONV, to, conv);

702        /* The derived-to-base conversion indicates the initialization

703          of a parameter with base type from an object of a derived

704          type. A temporary object is created to hold the result of

705          the conversion.  */

706        NEED_TEMPORARY_P (conv) = 1;

707      }

708      else

709        return 0;

710   

711       return conv;

712    }

 

Code between line 695 to 707 handles the case of derived-to-base conversion, which is defined by terms 6 of the clause “Implicit conversion sequeunces”.

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
for Policy and Practice, 2018, 17(2): 179-195. In this article, Victor Wang Chen Neo examines the implementation of school-based curriculum development (SBCD) in Singapore. SBCD is a process where schools are given greater autonomy to develop and implement their own curriculum, rather than following a standardized national curriculum. The author begins by providing an overview of the history of curriculum development in Singapore, and how the shift towards SBCD came about. He then presents the findings of a study that he conducted, which involved interviews with school leaders who had implemented SBCD in their schools. The author identifies several factors that influenced the successful implementation of SBCD in these schools. These include strong leadership, a clear vision and direction for the school, and a focus on student learning and development. The author also highlights the importance of teacher training and support, as well as collaboration and communication among all stakeholders involved in the curriculum development process. However, the author also notes some challenges that schools face when implementing SBCD. These include a lack of resources, such as time and funding, as well as the need to balance autonomy with accountability to ensure that the curriculum developed meets national standards. Overall, the author suggests that SBCD has the potential to improve the quality of education in Singapore by allowing schools to tailor their curriculum to the needs and interests of their students. However, he also calls for continued support and guidance from the government to ensure that schools are able to implement SBCD effectively.

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值