| /* Build expressions with type checking for C++ compiler. |
| Copyright (C) 1987, 88, 89, 92-97, 1998 Free Software Foundation, Inc. |
| Hacked by Michael Tiemann (tiemann@cygnus.com) |
| |
| This file is part of GNU CC. |
| |
| GNU CC is free software; you can redistribute it and/or modify |
| it under the terms of the GNU General Public License as published by |
| the Free Software Foundation; either version 2, or (at your option) |
| any later version. |
| |
| GNU CC is distributed in the hope that it will be useful, |
| but WITHOUT ANY WARRANTY; without even the implied warranty of |
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| GNU General Public License for more details. |
| |
| You should have received a copy of the GNU General Public License |
| along with GNU CC; see the file COPYING. If not, write to |
| the Free Software Foundation, 59 Temple Place - Suite 330, |
| Boston, MA 02111-1307, USA. */ |
| |
| |
| /* This file is part of the C++ front end. |
| It contains routines to build C++ expressions given their operands, |
| including computing the types of the result, C and C++ specific error |
| checks, and some optimization. |
| |
| There are also routines to build RETURN_STMT nodes and CASE_STMT nodes, |
| and to process initializations in declarations (since they work |
| like a strange sort of assignment). */ |
| |
| #include "config.h" |
| #include "system.h" |
| #include "tree.h" |
| #include "rtl.h" |
| #include "cp-tree.h" |
| #include "flags.h" |
| #include "output.h" |
| #include "expr.h" |
| #include "toplev.h" |
| |
| extern void compiler_error (); |
| |
| static tree convert_for_assignment PROTO((tree, tree, char*, tree, |
| int)); |
| static tree pointer_int_sum PROTO((enum tree_code, tree, tree)); |
| static tree rationalize_conditional_expr PROTO((enum tree_code, tree)); |
| static int comp_target_parms PROTO((tree, tree, int)); |
| static int comp_ptr_ttypes_real PROTO((tree, tree, int)); |
| static int comp_ptr_ttypes_const PROTO((tree, tree)); |
| static int comp_ptr_ttypes_reinterpret PROTO((tree, tree)); |
| static int comp_array_types PROTO((int (*) (tree, tree, int), tree, |
| tree, int)); |
| static tree build_ptrmemfunc1 PROTO((tree, tree, tree, tree, tree)); |
| static tree common_base_type PROTO((tree, tree)); |
| #if 0 |
| static tree convert_sequence PROTO((tree, tree)); |
| #endif |
| static tree lookup_anon_field PROTO((tree, tree)); |
| static tree pointer_diff PROTO((tree, tree, tree)); |
| static tree qualify_type PROTO((tree, tree)); |
| static tree get_delta_difference PROTO((tree, tree, int)); |
| |
| /* Return the target type of TYPE, which meas return T for: |
| T*, T&, T[], T (...), and otherwise, just T. */ |
| |
| tree |
| target_type (type) |
| tree type; |
| { |
| if (TREE_CODE (type) == REFERENCE_TYPE) |
| type = TREE_TYPE (type); |
| while (TREE_CODE (type) == POINTER_TYPE |
| || TREE_CODE (type) == ARRAY_TYPE |
| || TREE_CODE (type) == FUNCTION_TYPE |
| || TREE_CODE (type) == METHOD_TYPE |
| || TREE_CODE (type) == OFFSET_TYPE) |
| type = TREE_TYPE (type); |
| return type; |
| } |
| |
| /* Do `exp = require_complete_type (exp);' to make sure exp |
| does not have an incomplete type. (That includes void types.) */ |
| |
| tree |
| require_complete_type (value) |
| tree value; |
| { |
| tree type; |
| |
| if (processing_template_decl) |
| return value; |
| |
| if (TREE_CODE (value) == OVERLOAD) |
| type = unknown_type_node; |
| else |
| type = TREE_TYPE (value); |
| |
| /* First, detect a valid value with a complete type. */ |
| if (TYPE_SIZE (type) != 0 |
| && type != void_type_node |
| && ! (TYPE_LANG_SPECIFIC (type) |
| && (IS_SIGNATURE_POINTER (type) || IS_SIGNATURE_REFERENCE (type)) |
| && TYPE_SIZE (SIGNATURE_TYPE (type)) == 0)) |
| return value; |
| |
| /* If we see X::Y, we build an OFFSET_TYPE which has |
| not been laid out. Try to avoid an error by interpreting |
| it as this->X::Y, if reasonable. */ |
| if (TREE_CODE (value) == OFFSET_REF |
| && current_class_ref != 0 |
| && TREE_OPERAND (value, 0) == current_class_ref) |
| { |
| tree base, member = TREE_OPERAND (value, 1); |
| tree basetype = TYPE_OFFSET_BASETYPE (type); |
| my_friendly_assert (TREE_CODE (member) == FIELD_DECL, 305); |
| base = convert_pointer_to (basetype, current_class_ptr); |
| value = build (COMPONENT_REF, TREE_TYPE (member), |
| build_indirect_ref (base, NULL_PTR), member); |
| return require_complete_type (value); |
| } |
| |
| if (TYPE_SIZE (complete_type (type))) |
| return value; |
| else |
| { |
| incomplete_type_error (value, type); |
| return error_mark_node; |
| } |
| } |
| |
| /* Try to complete TYPE, if it is incomplete. For example, if TYPE is |
| a template instantiation, do the instantiation. Returns TYPE, |
| whether or not it could be completed, unless something goes |
| horribly wrong, in which case the error_mark_node is returned. */ |
| |
| tree |
| complete_type (type) |
| tree type; |
| { |
| if (type == NULL_TREE) |
| /* Rather than crash, we return something sure to cause an error |
| at some point. */ |
| return error_mark_node; |
| |
| if (type == error_mark_node || TYPE_SIZE (type) != NULL_TREE) |
| ; |
| else if (TREE_CODE (type) == ARRAY_TYPE && TYPE_DOMAIN (type)) |
| { |
| tree t = complete_type (TREE_TYPE (type)); |
| if (TYPE_SIZE (t) != NULL_TREE && ! processing_template_decl) |
| layout_type (type); |
| TYPE_NEEDS_CONSTRUCTING (type) |
| = TYPE_NEEDS_CONSTRUCTING (TYPE_MAIN_VARIANT (t)); |
| TYPE_NEEDS_DESTRUCTOR (type) |
| = TYPE_NEEDS_DESTRUCTOR (TYPE_MAIN_VARIANT (t)); |
| } |
| else if (IS_AGGR_TYPE (type) && CLASSTYPE_TEMPLATE_INSTANTIATION (type)) |
| instantiate_class_template (TYPE_MAIN_VARIANT (type)); |
| |
| return type; |
| } |
| |
| /* Like complete_type, but issue an error if the TYPE cannot be |
| completed. Returns NULL_TREE if the type cannot be made |
| complete. */ |
| |
| tree |
| complete_type_or_else (type) |
| tree type; |
| { |
| type = complete_type (type); |
| if (type != error_mark_node && !TYPE_SIZE (type)) |
| { |
| incomplete_type_error (NULL_TREE, type); |
| return NULL_TREE; |
| } |
| else |
| return type; |
| } |
| |
| /* Return truthvalue of whether type of EXP is instantiated. */ |
| |
| int |
| type_unknown_p (exp) |
| tree exp; |
| { |
| return (TREE_CODE (exp) == OVERLOAD |
| || TREE_CODE (exp) == TREE_LIST |
| || TREE_TYPE (exp) == unknown_type_node |
| || (TREE_CODE (TREE_TYPE (exp)) == OFFSET_TYPE |
| && TREE_TYPE (TREE_TYPE (exp)) == unknown_type_node)); |
| } |
| |
| /* Return truthvalue of whether T is function (or pfn) type. */ |
| |
| int |
| fntype_p (t) |
| tree t; |
| { |
| return (TREE_CODE (t) == FUNCTION_TYPE || TREE_CODE (t) == METHOD_TYPE |
| || (TREE_CODE (t) == POINTER_TYPE |
| && (TREE_CODE (TREE_TYPE (t)) == FUNCTION_TYPE |
| || TREE_CODE (TREE_TYPE (t)) == METHOD_TYPE))); |
| } |
| |
| /* Do `exp = require_instantiated_type (type, exp);' to make sure EXP |
| does not have an uninstantiated type. |
| TYPE is type to instantiate with, if uninstantiated. */ |
| |
| tree |
| require_instantiated_type (type, exp, errval) |
| tree type, exp, errval; |
| { |
| if (TREE_TYPE (exp) == NULL_TREE) |
| { |
| error ("argument list may not have an initializer list"); |
| return errval; |
| } |
| |
| if (TREE_CODE (exp) == OVERLOAD |
| || TREE_TYPE (exp) == unknown_type_node |
| || (TREE_CODE (TREE_TYPE (exp)) == OFFSET_TYPE |
| && TREE_TYPE (TREE_TYPE (exp)) == unknown_type_node)) |
| { |
| exp = instantiate_type (type, exp, 1); |
| if (TREE_TYPE (exp) == error_mark_node) |
| return errval; |
| } |
| return exp; |
| } |
| |
| /* Return a variant of TYPE which has all the type qualifiers of LIKE |
| as well as those of TYPE. */ |
| |
| static tree |
| qualify_type (type, like) |
| tree type, like; |
| { |
| int constflag = TYPE_READONLY (type) || TYPE_READONLY (like); |
| int volflag = TYPE_VOLATILE (type) || TYPE_VOLATILE (like); |
| /* @@ Must do member pointers here. */ |
| return cp_build_type_variant (type, constflag, volflag); |
| } |
| |
| /* Return the common type of two parameter lists. |
| We assume that comptypes has already been done and returned 1; |
| if that isn't so, this may crash. |
| |
| As an optimization, free the space we allocate if the parameter |
| lists are already common. */ |
| |
| tree |
| commonparms (p1, p2) |
| tree p1, p2; |
| { |
| tree oldargs = p1, newargs, n; |
| int i, len; |
| int any_change = 0; |
| char *first_obj = (char *) oballoc (0); |
| |
| len = list_length (p1); |
| newargs = tree_last (p1); |
| |
| if (newargs == void_list_node) |
| i = 1; |
| else |
| { |
| i = 0; |
| newargs = 0; |
| } |
| |
| for (; i < len; i++) |
| newargs = tree_cons (NULL_TREE, NULL_TREE, newargs); |
| |
| n = newargs; |
| |
| for (i = 0; p1; |
| p1 = TREE_CHAIN (p1), p2 = TREE_CHAIN (p2), n = TREE_CHAIN (n), i++) |
| { |
| if (TREE_PURPOSE (p1) && !TREE_PURPOSE (p2)) |
| { |
| TREE_PURPOSE (n) = TREE_PURPOSE (p1); |
| any_change = 1; |
| } |
| else if (! TREE_PURPOSE (p1)) |
| { |
| if (TREE_PURPOSE (p2)) |
| { |
| TREE_PURPOSE (n) = TREE_PURPOSE (p2); |
| any_change = 1; |
| } |
| } |
| else |
| { |
| if (1 != simple_cst_equal (TREE_PURPOSE (p1), TREE_PURPOSE (p2))) |
| any_change = 1; |
| TREE_PURPOSE (n) = TREE_PURPOSE (p2); |
| } |
| if (TREE_VALUE (p1) != TREE_VALUE (p2)) |
| { |
| any_change = 1; |
| TREE_VALUE (n) = common_type (TREE_VALUE (p1), TREE_VALUE (p2)); |
| } |
| else |
| TREE_VALUE (n) = TREE_VALUE (p1); |
| } |
| if (! any_change) |
| { |
| obfree (first_obj); |
| return oldargs; |
| } |
| |
| return newargs; |
| } |
| |
| /* Given a type, perhaps copied for a typedef, |
| find the "original" version of it. */ |
| tree |
| original_type (t) |
| tree t; |
| { |
| while (TYPE_NAME (t) != NULL_TREE) |
| { |
| tree x = TYPE_NAME (t); |
| if (TREE_CODE (x) != TYPE_DECL) |
| break; |
| x = DECL_ORIGINAL_TYPE (x); |
| if (x == NULL_TREE) |
| break; |
| t = x; |
| } |
| return t; |
| } |
| |
| /* Return the common type of two types. |
| We assume that comptypes has already been done and returned 1; |
| if that isn't so, this may crash. |
| |
| This is the type for the result of most arithmetic operations |
| if the operands have the given two types. |
| |
| We do not deal with enumeral types here because they have already been |
| converted to integer types. */ |
| |
| tree |
| common_type (t1, t2) |
| tree t1, t2; |
| { |
| register enum tree_code code1; |
| register enum tree_code code2; |
| tree attributes; |
| |
| /* Save time if the two types are the same. */ |
| if (t1 == t2) |
| return t1; |
| t1 = original_type (t1); |
| t2 = original_type (t2); |
| if (t1 == t2) |
| return t1; |
| |
| /* If one type is nonsense, use the other. */ |
| if (t1 == error_mark_node) |
| return t2; |
| if (t2 == error_mark_node) |
| return t1; |
| |
| /* Merge the attributes. */ |
| attributes = merge_machine_type_attributes (t1, t2); |
| |
| { register tree a1, a2; |
| a1 = TYPE_ATTRIBUTES (t1); |
| a2 = TYPE_ATTRIBUTES (t2); |
| |
| /* Either one unset? Take the set one. */ |
| |
| if (!(attributes = a1)) |
| attributes = a2; |
| |
| /* One that completely contains the other? Take it. */ |
| |
| else if (a2 && !attribute_list_contained (a1, a2)) |
| { |
| if (attribute_list_contained (a2, a1)) |
| attributes = a2; |
| else |
| { |
| /* Pick the longest list, and hang on the other list. */ |
| /* ??? For the moment we punt on the issue of attrs with args. */ |
| |
| if (list_length (a1) < list_length (a2)) |
| attributes = a2, a2 = a1; |
| |
| for (; a2; a2 = TREE_CHAIN (a2)) |
| if (lookup_attribute (IDENTIFIER_POINTER (TREE_PURPOSE (a2)), |
| attributes) == NULL_TREE) |
| { |
| a1 = copy_node (a2); |
| TREE_CHAIN (a1) = attributes; |
| attributes = a1; |
| } |
| } |
| } |
| } |
| |
| /* Treat an enum type as the unsigned integer type of the same width. */ |
| |
| if (TREE_CODE (t1) == ENUMERAL_TYPE) |
| t1 = type_for_size (TYPE_PRECISION (t1), 1); |
| if (TREE_CODE (t2) == ENUMERAL_TYPE) |
| t2 = type_for_size (TYPE_PRECISION (t2), 1); |
| |
| if (TYPE_PTRMEMFUNC_P (t1)) |
| t1 = TYPE_PTRMEMFUNC_FN_TYPE (t1); |
| if (TYPE_PTRMEMFUNC_P (t2)) |
| t2 = TYPE_PTRMEMFUNC_FN_TYPE (t2); |
| |
| code1 = TREE_CODE (t1); |
| code2 = TREE_CODE (t2); |
| |
| /* If one type is complex, form the common type of the non-complex |
| components, then make that complex. Use T1 or T2 if it is the |
| required type. */ |
| if (code1 == COMPLEX_TYPE || code2 == COMPLEX_TYPE) |
| { |
| tree subtype1 = code1 == COMPLEX_TYPE ? TREE_TYPE (t1) : t1; |
| tree subtype2 = code2 == COMPLEX_TYPE ? TREE_TYPE (t2) : t2; |
| tree subtype = common_type (subtype1, subtype2); |
| |
| if (code1 == COMPLEX_TYPE && TREE_TYPE (t1) == subtype) |
| return build_type_attribute_variant (t1, attributes); |
| else if (code2 == COMPLEX_TYPE && TREE_TYPE (t2) == subtype) |
| return build_type_attribute_variant (t2, attributes); |
| else |
| return build_type_attribute_variant (build_complex_type (subtype), |
| attributes); |
| } |
| |
| switch (code1) |
| { |
| case INTEGER_TYPE: |
| case REAL_TYPE: |
| /* If only one is real, use it as the result. */ |
| |
| if (code1 == REAL_TYPE && code2 != REAL_TYPE) |
| return build_type_attribute_variant (t1, attributes); |
| |
| if (code2 == REAL_TYPE && code1 != REAL_TYPE) |
| return build_type_attribute_variant (t2, attributes); |
| |
| /* Both real or both integers; use the one with greater precision. */ |
| |
| if (TYPE_PRECISION (t1) > TYPE_PRECISION (t2)) |
| return build_type_attribute_variant (t1, attributes); |
| else if (TYPE_PRECISION (t2) > TYPE_PRECISION (t1)) |
| return build_type_attribute_variant (t2, attributes); |
| |
| /* Same precision. Prefer longs to ints even when same size. */ |
| |
| if (TYPE_MAIN_VARIANT (t1) == long_unsigned_type_node |
| || TYPE_MAIN_VARIANT (t2) == long_unsigned_type_node) |
| return build_type_attribute_variant (long_unsigned_type_node, |
| attributes); |
| |
| if (TYPE_MAIN_VARIANT (t1) == long_integer_type_node |
| || TYPE_MAIN_VARIANT (t2) == long_integer_type_node) |
| { |
| /* But preserve unsignedness from the other type, |
| since long cannot hold all the values of an unsigned int. */ |
| if (TREE_UNSIGNED (t1) || TREE_UNSIGNED (t2)) |
| t1 = long_unsigned_type_node; |
| else |
| t1 = long_integer_type_node; |
| return build_type_attribute_variant (t1, attributes); |
| } |
| |
| if (TYPE_MAIN_VARIANT (t1) == long_double_type_node |
| || TYPE_MAIN_VARIANT (t2) == long_double_type_node) |
| return build_type_attribute_variant (long_double_type_node, |
| attributes); |
| |
| /* Otherwise prefer the unsigned one. */ |
| |
| if (TREE_UNSIGNED (t1)) |
| return build_type_attribute_variant (t1, attributes); |
| else |
| return build_type_attribute_variant (t2, attributes); |
| |
| case POINTER_TYPE: |
| case REFERENCE_TYPE: |
| /* For two pointers, do this recursively on the target type, |
| and combine the qualifiers of the two types' targets. */ |
| /* This code was turned off; I don't know why. |
| But ANSI C++ specifies doing this with the qualifiers. |
| So I turned it on again. */ |
| { |
| tree tt1 = TYPE_MAIN_VARIANT (TREE_TYPE (t1)); |
| tree tt2 = TYPE_MAIN_VARIANT (TREE_TYPE (t2)); |
| int constp |
| = TYPE_READONLY (TREE_TYPE (t1)) || TYPE_READONLY (TREE_TYPE (t2)); |
| int volatilep |
| = TYPE_VOLATILE (TREE_TYPE (t1)) || TYPE_VOLATILE (TREE_TYPE (t2)); |
| tree target; |
| |
| if (tt1 == tt2) |
| target = tt1; |
| else if (tt1 == void_type_node || tt2 == void_type_node) |
| target = void_type_node; |
| else if (tt1 == unknown_type_node) |
| target = tt2; |
| else if (tt2 == unknown_type_node) |
| target = tt1; |
| else |
| target = common_type (tt1, tt2); |
| |
| target = cp_build_type_variant (target, constp, volatilep); |
| if (code1 == POINTER_TYPE) |
| t1 = build_pointer_type (target); |
| else |
| t1 = build_reference_type (target); |
| t1 = build_type_attribute_variant (t1, attributes); |
| |
| if (TREE_CODE (target) == METHOD_TYPE) |
| t1 = build_ptrmemfunc_type (t1); |
| |
| return t1; |
| } |
| |
| case ARRAY_TYPE: |
| { |
| tree elt = common_type (TREE_TYPE (t1), TREE_TYPE (t2)); |
| /* Save space: see if the result is identical to one of the args. */ |
| if (elt == TREE_TYPE (t1) && TYPE_DOMAIN (t1)) |
| return build_type_attribute_variant (t1, attributes); |
| if (elt == TREE_TYPE (t2) && TYPE_DOMAIN (t2)) |
| return build_type_attribute_variant (t2, attributes); |
| /* Merge the element types, and have a size if either arg has one. */ |
| t1 = build_cplus_array_type |
| (elt, TYPE_DOMAIN (TYPE_DOMAIN (t1) ? t1 : t2)); |
| return build_type_attribute_variant (t1, attributes); |
| } |
| |
| case FUNCTION_TYPE: |
| /* Function types: prefer the one that specified arg types. |
| If both do, merge the arg types. Also merge the return types. */ |
| { |
| tree valtype = common_type (TREE_TYPE (t1), TREE_TYPE (t2)); |
| tree p1 = TYPE_ARG_TYPES (t1); |
| tree p2 = TYPE_ARG_TYPES (t2); |
| tree rval, raises; |
| |
| /* Save space: see if the result is identical to one of the args. */ |
| if (valtype == TREE_TYPE (t1) && ! p2) |
| return build_type_attribute_variant (t1, attributes); |
| if (valtype == TREE_TYPE (t2) && ! p1) |
| return build_type_attribute_variant (t2, attributes); |
| |
| /* Simple way if one arg fails to specify argument types. */ |
| if (p1 == NULL_TREE || TREE_VALUE (p1) == void_type_node) |
| { |
| rval = build_function_type (valtype, p2); |
| if ((raises = TYPE_RAISES_EXCEPTIONS (t2))) |
| rval = build_exception_variant (rval, raises); |
| return build_type_attribute_variant (rval, attributes); |
| } |
| raises = TYPE_RAISES_EXCEPTIONS (t1); |
| if (p2 == NULL_TREE || TREE_VALUE (p2) == void_type_node) |
| { |
| rval = build_function_type (valtype, p1); |
| if (raises) |
| rval = build_exception_variant (rval, raises); |
| return build_type_attribute_variant (rval, attributes); |
| } |
| |
| rval = build_function_type (valtype, commonparms (p1, p2)); |
| rval = build_exception_variant (rval, raises); |
| return build_type_attribute_variant (rval, attributes); |
| } |
| |
| case RECORD_TYPE: |
| case UNION_TYPE: |
| t1 = TYPE_MAIN_VARIANT (t1); |
| t2 = TYPE_MAIN_VARIANT (t2); |
| |
| if (DERIVED_FROM_P (t1, t2) && binfo_or_else (t1, t2)) |
| return build_type_attribute_variant (t1, attributes); |
| else if (binfo_or_else (t2, t1)) |
| return build_type_attribute_variant (t2, attributes); |
| else |
| compiler_error ("common_type called with uncommon aggregate types"); |
| |
| case METHOD_TYPE: |
| if (TREE_CODE (TREE_TYPE (t1)) == TREE_CODE (TREE_TYPE (t2))) |
| { |
| /* Get this value the long way, since TYPE_METHOD_BASETYPE |
| is just the main variant of this. */ |
| tree basetype; |
| tree raises, t3; |
| |
| tree b1 = TYPE_OFFSET_BASETYPE (t1); |
| tree b2 = TYPE_OFFSET_BASETYPE (t2); |
| |
| if (comptypes (b1, b2, 1) |
| || (DERIVED_FROM_P (b1, b2) && binfo_or_else (b1, b2))) |
| basetype = TREE_TYPE (TREE_VALUE (TYPE_ARG_TYPES (t2))); |
| else |
| { |
| if (binfo_or_else (b2, b1) == NULL_TREE) |
| compiler_error ("common_type called with uncommon method types"); |
| basetype = TREE_TYPE (TREE_VALUE (TYPE_ARG_TYPES (t1))); |
| } |
| |
| raises = TYPE_RAISES_EXCEPTIONS (t1); |
| |
| /* If this was a member function type, get back to the |
| original type of type member function (i.e., without |
| the class instance variable up front. */ |
| t1 = build_function_type (TREE_TYPE (t1), |
| TREE_CHAIN (TYPE_ARG_TYPES (t1))); |
| t2 = build_function_type (TREE_TYPE (t2), |
| TREE_CHAIN (TYPE_ARG_TYPES (t2))); |
| t3 = common_type (t1, t2); |
| t3 = build_cplus_method_type (basetype, TREE_TYPE (t3), |
| TYPE_ARG_TYPES (t3)); |
| t1 = build_exception_variant (t3, raises); |
| } |
| else |
| compiler_error ("common_type called with uncommon method types"); |
| |
| return build_type_attribute_variant (t1, attributes); |
| |
| case OFFSET_TYPE: |
| if (TREE_TYPE (t1) == TREE_TYPE (t2)) |
| { |
| tree b1 = TYPE_OFFSET_BASETYPE (t1); |
| tree b2 = TYPE_OFFSET_BASETYPE (t2); |
| |
| if (comptypes (b1, b2, 1) |
| || (DERIVED_FROM_P (b1, b2) && binfo_or_else (b1, b2))) |
| return build_type_attribute_variant (t2, attributes); |
| else if (binfo_or_else (b2, b1)) |
| return build_type_attribute_variant (t1, attributes); |
| } |
| compiler_error ("common_type called with uncommon member types"); |
| |
| default: |
| return build_type_attribute_variant (t1, attributes); |
| } |
| } |
| |
| /* Return 1 if TYPE1 and TYPE2 raise the same exceptions. */ |
| |
| int |
| compexcepttypes (t1, t2) |
| tree t1, t2; |
| { |
| return TYPE_RAISES_EXCEPTIONS (t1) == TYPE_RAISES_EXCEPTIONS (t2); |
| } |
| |
| static int |
| comp_array_types (cmp, t1, t2, strict) |
| register int (*cmp) PROTO((tree, tree, int)); |
| tree t1, t2; |
| int strict; |
| { |
| tree d1 = TYPE_DOMAIN (t1); |
| tree d2 = TYPE_DOMAIN (t2); |
| |
| /* Target types must match incl. qualifiers. */ |
| if (!(TREE_TYPE (t1) == TREE_TYPE (t2) |
| || (*cmp) (TREE_TYPE (t1), TREE_TYPE (t2), strict))) |
| return 0; |
| |
| /* Sizes must match unless one is missing or variable. */ |
| if (d1 == 0 || d2 == 0 || d1 == d2 |
| || TREE_CODE (TYPE_MIN_VALUE (d1)) != INTEGER_CST |
| || TREE_CODE (TYPE_MIN_VALUE (d2)) != INTEGER_CST |
| || TREE_CODE (TYPE_MAX_VALUE (d1)) != INTEGER_CST |
| || TREE_CODE (TYPE_MAX_VALUE (d2)) != INTEGER_CST) |
| return 1; |
| |
| return ((TREE_INT_CST_LOW (TYPE_MIN_VALUE (d1)) |
| == TREE_INT_CST_LOW (TYPE_MIN_VALUE (d2))) |
| && (TREE_INT_CST_HIGH (TYPE_MIN_VALUE (d1)) |
| == TREE_INT_CST_HIGH (TYPE_MIN_VALUE (d2))) |
| && (TREE_INT_CST_LOW (TYPE_MAX_VALUE (d1)) |
| == TREE_INT_CST_LOW (TYPE_MAX_VALUE (d2))) |
| && (TREE_INT_CST_HIGH (TYPE_MAX_VALUE (d1)) |
| == TREE_INT_CST_HIGH (TYPE_MAX_VALUE (d2)))); |
| } |
| |
| /* Return 1 if TYPE1 and TYPE2 are compatible types for assignment |
| or various other operations. This is what ANSI C++ speaks of as |
| "being the same". |
| |
| For C++: argument STRICT says we should be strict about this |
| comparison: |
| |
| 2 : strict, except that if one type is a reference and |
| the other is not, compare the target type of the |
| reference to the type that's not a reference (ARM, p308). |
| This is used for checking for invalid overloading. |
| 1 : strict (compared according to ANSI C) |
| This is used for checking whether two function decls match. |
| 0 : <= (compared according to C++) |
| -1: <= or >= (relaxed) |
| |
| Otherwise, pointers involving base classes and derived classes can |
| be mixed as valid: i.e. a pointer to a derived class may be converted |
| to a pointer to one of its base classes, as per C++. A pointer to |
| a derived class may be passed as a parameter to a function expecting a |
| pointer to a base classes. These allowances do not commute. In this |
| case, TYPE1 is assumed to be the base class, and TYPE2 is assumed to |
| be the derived class. */ |
| |
| int |
| comptypes (type1, type2, strict) |
| tree type1, type2; |
| int strict; |
| { |
| register tree t1 = type1; |
| register tree t2 = type2; |
| int attrval, val; |
| |
| /* Suppress errors caused by previously reported errors */ |
| |
| if (t1 == t2) |
| return 1; |
| |
| /* This should never happen. */ |
| my_friendly_assert (t1 != error_mark_node, 307); |
| |
| if (t2 == error_mark_node) |
| return 0; |
| |
| if (strict < 0) |
| { |
| /* Treat an enum type as the unsigned integer type of the same width. */ |
| |
| if (TREE_CODE (t1) == ENUMERAL_TYPE) |
| t1 = type_for_size (TYPE_PRECISION (t1), 1); |
| if (TREE_CODE (t2) == ENUMERAL_TYPE) |
| t2 = type_for_size (TYPE_PRECISION (t2), 1); |
| |
| if (t1 == t2) |
| return 1; |
| } |
| |
| if (TYPE_PTRMEMFUNC_P (t1)) |
| t1 = TYPE_PTRMEMFUNC_FN_TYPE (t1); |
| if (TYPE_PTRMEMFUNC_P (t2)) |
| t2 = TYPE_PTRMEMFUNC_FN_TYPE (t2); |
| |
| /* Different classes of types can't be compatible. */ |
| |
| if (TREE_CODE (t1) != TREE_CODE (t2)) |
| { |
| if (strict == 2 |
| && ((TREE_CODE (t1) == REFERENCE_TYPE) |
| ^ (TREE_CODE (t2) == REFERENCE_TYPE))) |
| { |
| if (TREE_CODE (t1) == REFERENCE_TYPE) |
| return comptypes (TREE_TYPE (t1), t2, 1); |
| return comptypes (t1, TREE_TYPE (t2), 1); |
| } |
| |
| return 0; |
| } |
| if (strict > 1) |
| strict = 1; |
| |
| /* Qualifiers must match. */ |
| |
| if (TYPE_READONLY (t1) != TYPE_READONLY (t2)) |
| return 0; |
| if (TYPE_VOLATILE (t1) != TYPE_VOLATILE (t2)) |
| return 0; |
| if (strict > 0 && TYPE_FOR_JAVA (t1) != TYPE_FOR_JAVA (t2)) |
| return 0; |
| |
| /* Allow for two different type nodes which have essentially the same |
| definition. Note that we already checked for equality of the type |
| qualifiers (just above). */ |
| |
| if (TYPE_MAIN_VARIANT (t1) == TYPE_MAIN_VARIANT (t2)) |
| return 1; |
| |
| /* ??? COMP_TYPE_ATTRIBUTES is currently useless for variables as each |
| attribute is its own main variant (`val' will remain 0). */ |
| #ifndef COMP_TYPE_ATTRIBUTES |
| #define COMP_TYPE_ATTRIBUTES(t1,t2) 1 |
| #endif |
| |
| /* 1 if no need for warning yet, 2 if warning cause has been seen. */ |
| if (! (attrval = COMP_TYPE_ATTRIBUTES (t1, t2))) |
| return 0; |
| |
| /* 1 if no need for warning yet, 2 if warning cause has been seen. */ |
| val = 0; |
| |
| switch (TREE_CODE (t1)) |
| { |
| case TEMPLATE_TEMPLATE_PARM: |
| if (TEMPLATE_TYPE_IDX (t1) != TEMPLATE_TYPE_IDX (t2) |
| || TEMPLATE_TYPE_LEVEL (t1) != TEMPLATE_TYPE_LEVEL (t2)) |
| return 0; |
| if (! comp_template_parms (DECL_TEMPLATE_PARMS (TYPE_NAME (t1)), |
| DECL_TEMPLATE_PARMS (TYPE_NAME (t2)))) |
| return 0; |
| if (! CLASSTYPE_TEMPLATE_INFO (t1) && ! CLASSTYPE_TEMPLATE_INFO (t2)) |
| return 1; |
| /* Don't check inheritance. */ |
| strict = 1; |
| /* fall through */ |
| |
| case RECORD_TYPE: |
| case UNION_TYPE: |
| if (CLASSTYPE_TEMPLATE_INFO (t1) && CLASSTYPE_TEMPLATE_INFO (t2) |
| && (CLASSTYPE_TI_TEMPLATE (t1) == CLASSTYPE_TI_TEMPLATE (t2) |
| || TREE_CODE (t1) == TEMPLATE_TEMPLATE_PARM)) |
| return comp_template_args (CLASSTYPE_TI_ARGS (t1), |
| CLASSTYPE_TI_ARGS (t2)); |
| if (strict <= 0) |
| goto look_hard; |
| return 0; |
| |
| case OFFSET_TYPE: |
| val = (comptypes (build_pointer_type (TYPE_OFFSET_BASETYPE (t1)), |
| build_pointer_type (TYPE_OFFSET_BASETYPE (t2)), strict) |
| && comptypes (TREE_TYPE (t1), TREE_TYPE (t2), strict)); |
| break; |
| |
| case METHOD_TYPE: |
| if (! compexcepttypes (t1, t2)) |
| return 0; |
| |
| /* This case is anti-symmetrical! |
| One can pass a base member (or member function) |
| to something expecting a derived member (or member function), |
| but not vice-versa! */ |
| |
| val = (comptypes (TREE_TYPE (t1), TREE_TYPE (t2), strict) |
| && compparms (TYPE_ARG_TYPES (t1), |
| TYPE_ARG_TYPES (t2), strict)); |
| break; |
| |
| case POINTER_TYPE: |
| case REFERENCE_TYPE: |
| t1 = TREE_TYPE (t1); |
| t2 = TREE_TYPE (t2); |
| if (t1 == t2) |
| { |
| val = 1; |
| break; |
| } |
| if (strict <= 0) |
| { |
| if (TREE_CODE (t1) == RECORD_TYPE && TREE_CODE (t2) == RECORD_TYPE) |
| { |
| int rval; |
| look_hard: |
| rval = t1 == t2 || DERIVED_FROM_P (t1, t2); |
| |
| if (rval) |
| { |
| val = 1; |
| break; |
| } |
| if (strict < 0) |
| { |
| val = DERIVED_FROM_P (t2, t1); |
| break; |
| } |
| } |
| return 0; |
| } |
| else |
| val = comptypes (t1, t2, strict); |
| break; |
| |
| case FUNCTION_TYPE: |
| if (! compexcepttypes (t1, t2)) |
| return 0; |
| |
| val = ((TREE_TYPE (t1) == TREE_TYPE (t2) |
| || comptypes (TREE_TYPE (t1), TREE_TYPE (t2), strict)) |
| && compparms (TYPE_ARG_TYPES (t1), TYPE_ARG_TYPES (t2), strict)); |
| break; |
| |
| case ARRAY_TYPE: |
| /* Target types must match incl. qualifiers. */ |
| val = comp_array_types (comptypes, t1, t2, strict); |
| break; |
| |
| case TEMPLATE_TYPE_PARM: |
| return TEMPLATE_TYPE_IDX (t1) == TEMPLATE_TYPE_IDX (t2) |
| && TEMPLATE_TYPE_LEVEL (t1) == TEMPLATE_TYPE_LEVEL (t2); |
| |
| case TYPENAME_TYPE: |
| if (TYPE_IDENTIFIER (t1) != TYPE_IDENTIFIER (t2)) |
| return 0; |
| return comptypes (TYPE_CONTEXT (t1), TYPE_CONTEXT (t2), 1); |
| |
| default: |
| break; |
| } |
| return attrval == 2 && val == 1 ? 2 : val; |
| } |
| |
| /* Return 1 or -1 if TTL and TTR are pointers to types that are equivalent, |
| ignoring their qualifiers, 0 if not. Return 1 means that TTR can be |
| converted to TTL. Return -1 means that TTL can be converted to TTR but |
| not vice versa. |
| |
| NPTRS is the number of pointers we can strip off and keep cool. |
| This is used to permit (for aggr A, aggr B) A, B* to convert to A*, |
| but to not permit B** to convert to A**. |
| |
| This should go away. Callers should use can_convert or something |
| similar instead. (jason 17 Apr 1997) */ |
| |
| int |
| comp_target_types (ttl, ttr, nptrs) |
| tree ttl, ttr; |
| int nptrs; |
| { |
| ttl = TYPE_MAIN_VARIANT (ttl); |
| ttr = TYPE_MAIN_VARIANT (ttr); |
| if (ttl == ttr) |
| return 1; |
| |
| if (TREE_CODE (ttr) != TREE_CODE (ttl)) |
| return 0; |
| |
| if (TREE_CODE (ttr) == POINTER_TYPE |
| || (TREE_CODE (ttr) == REFERENCE_TYPE)) |
| { |
| int is_ptr = TREE_CODE (ttr) == POINTER_TYPE; |
| |
| ttl = TREE_TYPE (ttl); |
| ttr = TREE_TYPE (ttr); |
| |
| if (nptrs > 0 && is_ptr) |
| { |
| if (TREE_CODE (ttl) == UNKNOWN_TYPE |
| || TREE_CODE (ttr) == UNKNOWN_TYPE) |
| return 1; |
| else if (TREE_CODE (ttl) == VOID_TYPE |
| && TREE_CODE (ttr) != FUNCTION_TYPE |
| && TREE_CODE (ttr) != METHOD_TYPE |
| && TREE_CODE (ttr) != OFFSET_TYPE) |
| return 1; |
| else if (TREE_CODE (ttr) == VOID_TYPE |
| && TREE_CODE (ttl) != FUNCTION_TYPE |
| && TREE_CODE (ttl) != METHOD_TYPE |
| && TREE_CODE (ttl) != OFFSET_TYPE) |
| return -1; |
| else if (TREE_CODE (ttl) == POINTER_TYPE |
| || TREE_CODE (ttl) == ARRAY_TYPE) |
| { |
| if (comp_ptr_ttypes (ttl, ttr)) |
| return 1; |
| else if (comp_ptr_ttypes (ttr, ttl)) |
| return -1; |
| return 0; |
| } |
| } |
| |
| /* Const and volatile mean something different for function types, |
| so the usual checks are not appropriate. */ |
| if (TREE_CODE (ttl) == FUNCTION_TYPE || TREE_CODE (ttl) == METHOD_TYPE) |
| return comp_target_types (ttl, ttr, nptrs - 1); |
| |
| /* Make sure that the cv-quals change only in the same direction as |
| the target type. */ |
| { |
| int t; |
| int c = TYPE_READONLY (ttl) - TYPE_READONLY (ttr); |
| int v = TYPE_VOLATILE (ttl) - TYPE_VOLATILE (ttr); |
| |
| if ((c > 0 && v < 0) || (c < 0 && v > 0)) |
| return 0; |
| |
| if (TYPE_MAIN_VARIANT (ttl) == TYPE_MAIN_VARIANT (ttr)) |
| return (c + v < 0) ? -1 : 1; |
| |
| t = comp_target_types (ttl, ttr, nptrs - 1); |
| if ((t == 1 && c + v >= 0) || (t == -1 && c + v <= 0)) |
| return t; |
| |
| return 0; |
| } |
| } |
| |
| if (TREE_CODE (ttr) == ARRAY_TYPE) |
| return comp_array_types (comp_target_types, ttl, ttr, 0); |
| else if (TREE_CODE (ttr) == FUNCTION_TYPE || TREE_CODE (ttr) == METHOD_TYPE) |
| { |
| tree argsl, argsr; |
| int saw_contra = 0; |
| |
| if (pedantic) |
| { |
| if (comptypes (TREE_TYPE (ttl), TREE_TYPE (ttr), 1) == 0) |
| return 0; |
| } |
| else |
| { |
| switch (comp_target_types (TREE_TYPE (ttl), TREE_TYPE (ttr), -1)) |
| { |
| case 0: |
| return 0; |
| case -1: |
| saw_contra = 1; |
| } |
| } |
| |
| argsl = TYPE_ARG_TYPES (ttl); |
| argsr = TYPE_ARG_TYPES (ttr); |
| |
| /* Compare 'this' here, not in comp_target_parms. */ |
| if (TREE_CODE (ttr) == METHOD_TYPE) |
| { |
| tree tl = TYPE_METHOD_BASETYPE (ttl); |
| tree tr = TYPE_METHOD_BASETYPE (ttr); |
| |
| if (comptypes (tr, tl, 0) == 0) |
| { |
| if (comptypes (tl, tr, 0)) |
| saw_contra = 1; |
| else |
| return 0; |
| } |
| |
| argsl = TREE_CHAIN (argsl); |
| argsr = TREE_CHAIN (argsr); |
| } |
| |
| switch (comp_target_parms (argsl, argsr, 1)) |
| { |
| case 0: |
| return 0; |
| case -1: |
| saw_contra = 1; |
| } |
| |
| return saw_contra ? -1 : 1; |
| } |
| /* for C++ */ |
| else if (TREE_CODE (ttr) == OFFSET_TYPE) |
| { |
| /* Contravariance: we can assign a pointer to base member to a pointer |
| to derived member. Note difference from simple pointer case, where |
| we can pass a pointer to derived to a pointer to base. */ |
| if (comptypes (TYPE_OFFSET_BASETYPE (ttr), |
| TYPE_OFFSET_BASETYPE (ttl), 0)) |
| return comp_target_types (TREE_TYPE (ttl), TREE_TYPE (ttr), nptrs); |
| else if (comptypes (TYPE_OFFSET_BASETYPE (ttl), |
| TYPE_OFFSET_BASETYPE (ttr), 0) |
| && comp_target_types (TREE_TYPE (ttl), TREE_TYPE (ttr), nptrs)) |
| return -1; |
| } |
| else if (IS_AGGR_TYPE (ttl)) |
| { |
| if (nptrs < 0) |
| return 0; |
| if (comptypes (build_pointer_type (ttl), build_pointer_type (ttr), 0)) |
| return 1; |
| if (comptypes (build_pointer_type (ttr), build_pointer_type (ttl), 0)) |
| return -1; |
| return 0; |
| } |
| |
| return 0; |
| } |
| |
| /* Returns 1 if TYPE1 is more cv-qualified than TYPE2, -1 if TYPE2 is |
| more cv-qualified that TYPE1, and 0 otherwise. */ |
| |
| int |
| comp_cv_qualification (type1, type2) |
| tree type1; |
| tree type2; |
| { |
| if (TYPE_READONLY (type1) == TYPE_READONLY (type2) |
| && TYPE_VOLATILE (type1) == TYPE_VOLATILE (type2)) |
| return 0; |
| |
| if (TYPE_READONLY (type1) >= TYPE_READONLY (type2) |
| && TYPE_VOLATILE (type1) >= TYPE_VOLATILE (type2)) |
| return 1; |
| |
| if (TYPE_READONLY (type2) >= TYPE_READONLY (type1) |
| && TYPE_VOLATILE (type2) >= TYPE_VOLATILE (type1)) |
| return -1; |
| |
| return 0; |
| } |
| |
| /* Returns 1 if the cv-qualification signature of TYPE1 is a proper |
| subset of the cv-qualification signature of TYPE2, and the types |
| are similar. Returns -1 if the other way 'round, and 0 otherwise. */ |
| |
| int |
| comp_cv_qual_signature (type1, type2) |
| tree type1; |
| tree type2; |
| { |
| if (comp_ptr_ttypes_real (type2, type1, -1)) |
| return 1; |
| else if (comp_ptr_ttypes_real (type1, type2, -1)) |
| return -1; |
| else |
| return 0; |
| } |
| |
| /* If two types share a common base type, return that basetype. |
| If there is not a unique most-derived base type, this function |
| returns ERROR_MARK_NODE. */ |
| |
| static tree |
| common_base_type (tt1, tt2) |
| tree tt1, tt2; |
| { |
| tree best = NULL_TREE; |
| int i; |
| |
| /* If one is a baseclass of another, that's good enough. */ |
| if (UNIQUELY_DERIVED_FROM_P (tt1, tt2)) |
| return tt1; |
| if (UNIQUELY_DERIVED_FROM_P (tt2, tt1)) |
| return tt2; |
| |
| /* Otherwise, try to find a unique baseclass of TT1 |
| that is shared by TT2, and follow that down. */ |
| for (i = CLASSTYPE_N_BASECLASSES (tt1)-1; i >= 0; i--) |
| { |
| tree basetype = TYPE_BINFO_BASETYPE (tt1, i); |
| tree trial = common_base_type (basetype, tt2); |
| if (trial) |
| { |
| if (trial == error_mark_node) |
| return trial; |
| if (best == NULL_TREE) |
| best = trial; |
| else if (best != trial) |
| return error_mark_node; |
| } |
| } |
| |
| /* Same for TT2. */ |
| for (i = CLASSTYPE_N_BASECLASSES (tt2)-1; i >= 0; i--) |
| { |
| tree basetype = TYPE_BINFO_BASETYPE (tt2, i); |
| tree trial = common_base_type (tt1, basetype); |
| if (trial) |
| { |
| if (trial == error_mark_node) |
| return trial; |
| if (best == NULL_TREE) |
| best = trial; |
| else if (best != trial) |
| return error_mark_node; |
| } |
| } |
| return best; |
| } |
| |
| /* Subroutines of `comptypes'. */ |
| |
| /* Return 1 if two parameter type lists PARMS1 and PARMS2 |
| are equivalent in the sense that functions with those parameter types |
| can have equivalent types. |
| If either list is empty, we win. |
| Otherwise, the two lists must be equivalent, element by element. |
| |
| C++: See comment above about TYPE1, TYPE2. |
| |
| STRICT is no longer used. */ |
| |
| int |
| compparms (parms1, parms2, strict) |
| tree parms1, parms2; |
| int strict; |
| { |
| register tree t1 = parms1, t2 = parms2; |
| |
| /* An unspecified parmlist matches any specified parmlist |
| whose argument types don't need default promotions. */ |
| |
| while (1) |
| { |
| if (t1 == 0 && t2 == 0) |
| return 1; |
| /* If one parmlist is shorter than the other, |
| they fail to match. */ |
| if (t1 == 0 || t2 == 0) |
| return 0; |
| if (! comptypes (TREE_VALUE (t2), TREE_VALUE (t1), 1)) |
| return 0; |
| |
| t1 = TREE_CHAIN (t1); |
| t2 = TREE_CHAIN (t2); |
| } |
| } |
| |
| /* This really wants return whether or not parameter type lists |
| would make their owning functions assignment compatible or not. |
| |
| The return value is like for comp_target_types. |
| |
| This should go away, possibly with the exception of the empty parmlist |
| conversion; there are no conversions between function types in C++. |
| (jason 17 Apr 1997) */ |
| |
| static int |
| comp_target_parms (parms1, parms2, strict) |
| tree parms1, parms2; |
| int strict; |
| { |
| register tree t1 = parms1, t2 = parms2; |
| int warn_contravariance = 0; |
| |
| /* In C, an unspecified parmlist matches any specified parmlist |
| whose argument types don't need default promotions. This is not |
| true for C++, but let's do it anyway for unfixed headers. */ |
| |
| if (t1 == 0 && t2 != 0) |
| { |
| cp_pedwarn ("ANSI C++ prohibits conversion from `(%#T)' to `(...)'", |
| parms2); |
| return self_promoting_args_p (t2); |
| } |
| if (t2 == 0) |
| return self_promoting_args_p (t1); |
| |
| for (; t1 || t2; t1 = TREE_CHAIN (t1), t2 = TREE_CHAIN (t2)) |
| { |
| tree p1, p2; |
| |
| /* If one parmlist is shorter than the other, |
| they fail to match, unless STRICT is <= 0. */ |
| if (t1 == 0 || t2 == 0) |
| { |
| if (strict > 0) |
| return 0; |
| if (strict < 0) |
| return 1 + warn_contravariance; |
| return ((t1 && TREE_PURPOSE (t1)) + warn_contravariance); |
| } |
| p1 = TREE_VALUE (t1); |
| p2 = TREE_VALUE (t2); |
| if (comptypes (p1, p2, 1)) |
| continue; |
| |
| if (pedantic) |
| return 0; |
| |
| if ((TREE_CODE (p1) == POINTER_TYPE && TREE_CODE (p2) == POINTER_TYPE) |
| || (TREE_CODE (p1) == REFERENCE_TYPE |
| && TREE_CODE (p2) == REFERENCE_TYPE)) |
| { |
| if (strict <= 0 |
| && (TYPE_MAIN_VARIANT (TREE_TYPE (p1)) |
| == TYPE_MAIN_VARIANT (TREE_TYPE (p2)))) |
| continue; |
| |
| /* The following is wrong for contravariance, |
| but many programs depend on it. */ |
| if (TREE_TYPE (p1) == void_type_node) |
| continue; |
| if (TREE_TYPE (p2) == void_type_node) |
| { |
| warn_contravariance = 1; |
| continue; |
| } |
| if (IS_AGGR_TYPE (TREE_TYPE (p1))) |
| { |
| if (comptypes (TYPE_MAIN_VARIANT (TREE_TYPE (p1)), |
| TYPE_MAIN_VARIANT (TREE_TYPE (p2)), 1) == 0) |
| return 0; |
| } |
| } |
| /* Note backwards order due to contravariance. */ |
| if (comp_target_types (p2, p1, 1) <= 0) |
| { |
| if (comp_target_types (p1, p2, 1) > 0) |
| { |
| warn_contravariance = 1; |
| continue; |
| } |
| if (strict != 0) |
| return 0; |
| } |
| } |
| return warn_contravariance ? -1 : 1; |
| } |
| |
| /* Return 1 if PARMS specifies a fixed number of parameters |
| and none of their types is affected by default promotions. */ |
| |
| int |
| self_promoting_args_p (parms) |
| tree parms; |
| { |
| register tree t; |
| for (t = parms; t; t = TREE_CHAIN (t)) |
| { |
| register tree type = TREE_VALUE (t); |
| |
| if (TREE_CHAIN (t) == 0 && type != void_type_node) |
| return 0; |
| |
| if (type == 0) |
| return 0; |
| |
| if (TYPE_MAIN_VARIANT (type) == float_type_node) |
| return 0; |
| |
| if (C_PROMOTING_INTEGER_TYPE_P (type)) |
| return 0; |
| } |
| return 1; |
| } |
| |
| /* Return an unsigned type the same as TYPE in other respects. |
| |
| C++: must make these work for type variants as well. */ |
| |
| tree |
| unsigned_type (type) |
| tree type; |
| { |
| tree type1 = TYPE_MAIN_VARIANT (type); |
| if (type1 == signed_char_type_node || type1 == char_type_node) |
| return unsigned_char_type_node; |
| if (type1 == integer_type_node) |
| return unsigned_type_node; |
| if (type1 == short_integer_type_node) |
| return short_unsigned_type_node; |
| if (type1 == long_integer_type_node) |
| return long_unsigned_type_node; |
| if (type1 == long_long_integer_type_node) |
| return long_long_unsigned_type_node; |
| if (type1 == intTI_type_node) |
| return unsigned_intTI_type_node; |
| if (type1 == intDI_type_node) |
| return unsigned_intDI_type_node; |
| if (type1 == intSI_type_node) |
| return unsigned_intSI_type_node; |
| if (type1 == intHI_type_node) |
| return unsigned_intHI_type_node; |
| if (type1 == intQI_type_node) |
| return unsigned_intQI_type_node; |
| |
| return signed_or_unsigned_type (1, type); |
| } |
| |
| /* Return a signed type the same as TYPE in other respects. */ |
| |
| tree |
| signed_type (type) |
| tree type; |
| { |
| tree type1 = TYPE_MAIN_VARIANT (type); |
| if (type1 == unsigned_char_type_node || type1 == char_type_node) |
| return signed_char_type_node; |
| if (type1 == unsigned_type_node) |
| return integer_type_node; |
| if (type1 == short_unsigned_type_node) |
| return short_integer_type_node; |
| if (type1 == long_unsigned_type_node) |
| return long_integer_type_node; |
| if (type1 == long_long_unsigned_type_node) |
| return long_long_integer_type_node; |
| if (type1 == unsigned_intTI_type_node) |
| return intTI_type_node; |
| if (type1 == unsigned_intDI_type_node) |
| return intDI_type_node; |
| if (type1 == unsigned_intSI_type_node) |
| return intSI_type_node; |
| if (type1 == unsigned_intHI_type_node) |
| return intHI_type_node; |
| if (type1 == unsigned_intQI_type_node) |
| return intQI_type_node; |
| |
| return signed_or_unsigned_type (0, type); |
| } |
| |
| /* Return a type the same as TYPE except unsigned or |
| signed according to UNSIGNEDP. */ |
| |
| tree |
| signed_or_unsigned_type (unsignedp, type) |
| int unsignedp; |
| tree type; |
| { |
| if (! INTEGRAL_TYPE_P (type) |
| || TREE_UNSIGNED (type) == unsignedp) |
| return type; |
| |
| if (TYPE_PRECISION (type) == TYPE_PRECISION (signed_char_type_node)) |
| return unsignedp ? unsigned_char_type_node : signed_char_type_node; |
| if (TYPE_PRECISION (type) == TYPE_PRECISION (integer_type_node)) |
| return unsignedp ? unsigned_type_node : integer_type_node; |
| if (TYPE_PRECISION (type) == TYPE_PRECISION (short_integer_type_node)) |
| return unsignedp ? short_unsigned_type_node : short_integer_type_node; |
| if (TYPE_PRECISION (type) == TYPE_PRECISION (long_integer_type_node)) |
| return unsignedp ? long_unsigned_type_node : long_integer_type_node; |
| if (TYPE_PRECISION (type) == TYPE_PRECISION (long_long_integer_type_node)) |
| return (unsignedp ? long_long_unsigned_type_node |
| : long_long_integer_type_node); |
| return type; |
| } |
| |
| /* Compute the value of the `sizeof' operator. */ |
| |
| tree |
| c_sizeof (type) |
| tree type; |
| { |
| enum tree_code code = TREE_CODE (type); |
| tree t; |
| |
| if (processing_template_decl) |
| return build_min (SIZEOF_EXPR, sizetype, type); |
| |
| if (code == FUNCTION_TYPE) |
| { |
| if (pedantic || warn_pointer_arith) |
| pedwarn ("ANSI C++ forbids taking the sizeof a function type"); |
| return size_int (1); |
| } |
| if (code == METHOD_TYPE) |
| { |
| if (pedantic || warn_pointer_arith) |
| pedwarn ("ANSI C++ forbids taking the sizeof a method type"); |
| return size_int (1); |
| } |
| if (code == VOID_TYPE) |
| { |
| if (pedantic || warn_pointer_arith) |
| pedwarn ("ANSI C++ forbids taking the sizeof a void type"); |
| return size_int (1); |
| } |
| if (code == ERROR_MARK) |
| return size_int (1); |
| |
| /* ARM $5.3.2: ``When applied to a reference, the result is the size of the |
| referenced object.'' */ |
| if (code == REFERENCE_TYPE) |
| type = TREE_TYPE (type); |
| |
| /* We couldn't find anything in the ARM or the draft standard that says, |
| one way or the other, if doing sizeof on something that doesn't have |
| an object associated with it is correct or incorrect. For example, if |
| you declare `struct S { char str[16]; };', and in your program do |
| a `sizeof (S::str)', should we flag that as an error or should we give |
| the size of it? Since it seems like a reasonable thing to do, we'll go |
| with giving the value. */ |
| if (code == OFFSET_TYPE) |
| type = TREE_TYPE (type); |
| |
| /* @@ This also produces an error for a signature ref. |
| In that case we should be able to do better. */ |
| if (IS_SIGNATURE (type)) |
| { |
| error ("`sizeof' applied to a signature type"); |
| return size_int (0); |
| } |
| |
| if (TYPE_SIZE (complete_type (type)) == 0) |
| { |
| cp_error ("`sizeof' applied to incomplete type `%T'", type); |
| return size_int (0); |
| } |
| |
| /* Convert in case a char is more than one unit. */ |
| t = size_binop (CEIL_DIV_EXPR, TYPE_SIZE (type), |
| size_int (TYPE_PRECISION (char_type_node))); |
| t = convert (sizetype, t); |
| /* size_binop does not put the constant in range, so do it now. */ |
| if (TREE_CODE (t) == INTEGER_CST && force_fit_type (t, 0)) |
| TREE_CONSTANT_OVERFLOW (t) = TREE_OVERFLOW (t) = 1; |
| return t; |
| } |
| |
| tree |
| expr_sizeof (e) |
| tree e; |
| { |
| if (processing_template_decl) |
| return build_min (SIZEOF_EXPR, sizetype, e); |
| |
| if (TREE_CODE (e) == COMPONENT_REF |
| && DECL_BIT_FIELD (TREE_OPERAND (e, 1))) |
| error ("sizeof applied to a bit-field"); |
| /* ANSI says arrays and functions are converted inside comma. |
| But we can't really convert them in build_compound_expr |
| because that would break commas in lvalues. |
| So do the conversion here if operand was a comma. */ |
| if (TREE_CODE (e) == COMPOUND_EXPR |
| && (TREE_CODE (TREE_TYPE (e)) == ARRAY_TYPE |
| || TREE_CODE (TREE_TYPE (e)) == FUNCTION_TYPE)) |
| e = default_conversion (e); |
| else if (TREE_CODE (e) == TREE_LIST) |
| { |
| tree t = TREE_VALUE (e); |
| if (t != NULL_TREE |
| && ((TREE_TYPE (t) |
| && TREE_CODE (TREE_TYPE (t)) == FUNCTION_TYPE) |
| || is_overloaded_fn (t))) |
| pedwarn ("ANSI C++ forbids taking the sizeof a function type"); |
| } |
| return c_sizeof (TREE_TYPE (e)); |
| } |
| |
| tree |
| c_sizeof_nowarn (type) |
| tree type; |
| { |
| enum tree_code code = TREE_CODE (type); |
| tree t; |
| |
| if (code == FUNCTION_TYPE |
| || code == METHOD_TYPE |
| || code == VOID_TYPE |
| || code == ERROR_MARK) |
| return size_int (1); |
| if (code == REFERENCE_TYPE) |
| type = TREE_TYPE (type); |
| |
| if (TYPE_SIZE (type) == 0) |
| return size_int (0); |
| |
| /* Convert in case a char is more than one unit. */ |
| t = size_binop (CEIL_DIV_EXPR, TYPE_SIZE (type), |
| size_int (TYPE_PRECISION (char_type_node))); |
| t = convert (sizetype, t); |
| force_fit_type (t, 0); |
| return t; |
| } |
| |
| /* Implement the __alignof keyword: Return the minimum required |
| alignment of TYPE, measured in bytes. */ |
| |
| tree |
| c_alignof (type) |
| tree type; |
| { |
| enum tree_code code = TREE_CODE (type); |
| tree t; |
| |
| if (processing_template_decl) |
| return build_min (ALIGNOF_EXPR, sizetype, type); |
| |
| if (code == FUNCTION_TYPE || code == METHOD_TYPE) |
| return size_int (FUNCTION_BOUNDARY / BITS_PER_UNIT); |
| |
| if (code == VOID_TYPE || code == ERROR_MARK) |
| return size_int (1); |
| |
| /* C++: this is really correct! */ |
| if (code == REFERENCE_TYPE) |
| type = TREE_TYPE (type); |
| |
| /* @@ This also produces an error for a signature ref. |
| In that case we should be able to do better. */ |
| if (IS_SIGNATURE (type)) |
| { |
| error ("`__alignof' applied to a signature type"); |
| return size_int (1); |
| } |
| |
| t = size_int (TYPE_ALIGN (type) / BITS_PER_UNIT); |
| force_fit_type (t, 0); |
| return t; |
| } |
| |
| /* Perform default promotions for C data used in expressions. |
| Arrays and functions are converted to pointers; |
| enumeral types or short or char, to int. |
| In addition, manifest constants symbols are replaced by their values. |
| |
| C++: this will automatically bash references to their target type. */ |
| |
| tree |
| decay_conversion (exp) |
| tree exp; |
| { |
| register tree type = TREE_TYPE (exp); |
| register enum tree_code code = TREE_CODE (type); |
| |
| if (code == OFFSET_TYPE) |
| { |
| if (TREE_CODE (exp) == OFFSET_REF) |
| return decay_conversion (resolve_offset_ref (exp)); |
| |
| type = TREE_TYPE (type); |
| code = TREE_CODE (type); |
| |
| if (type == unknown_type_node) |
| { |
| cp_pedwarn ("assuming & on overloaded member function"); |
| return build_unary_op (ADDR_EXPR, exp, 0); |
| } |
| } |
| |
| if (code == REFERENCE_TYPE) |
| { |
| exp = convert_from_reference (exp); |
| type = TREE_TYPE (exp); |
| code = TREE_CODE (type); |
| } |
| |
| /* Constants can be used directly unless they're not loadable. */ |
| if (TREE_CODE (exp) == CONST_DECL) |
| exp = DECL_INITIAL (exp); |
| /* Replace a nonvolatile const static variable with its value. */ |
| else if (TREE_READONLY_DECL_P (exp)) |
| { |
| exp = decl_constant_value (exp); |
| type = TREE_TYPE (exp); |
| } |
| |
| /* build_c_cast puts on a NOP_EXPR to make the result not an lvalue. |
| Leave such NOP_EXPRs, since RHS is being used in non-lvalue context. */ |
| |
| if (code == VOID_TYPE) |
| { |
| error ("void value not ignored as it ought to be"); |
| return error_mark_node; |
| } |
| if (code == FUNCTION_TYPE) |
| { |
| return build_unary_op (ADDR_EXPR, exp, 0); |
| } |
| if (code == METHOD_TYPE) |
| { |
| cp_pedwarn ("assuming & on `%E'", exp); |
| return build_unary_op (ADDR_EXPR, exp, 0); |
| } |
| if (code == ARRAY_TYPE) |
| { |
| register tree adr; |
| tree restype; |
| tree ptrtype; |
| int constp, volatilep; |
| |
| if (TREE_CODE (exp) == INDIRECT_REF) |
| { |
| /* Stripping away the INDIRECT_REF is not the right |
| thing to do for references... */ |
| tree inner = TREE_OPERAND (exp, 0); |
| if (TREE_CODE (TREE_TYPE (inner)) == REFERENCE_TYPE) |
| { |
| inner = build1 (CONVERT_EXPR, |
| build_pointer_type (TREE_TYPE |
| (TREE_TYPE (inner))), |
| inner); |
| TREE_CONSTANT (inner) = TREE_CONSTANT (TREE_OPERAND (inner, 0)); |
| } |
| return cp_convert (build_pointer_type (TREE_TYPE (type)), inner); |
| } |
| |
| if (TREE_CODE (exp) == COMPOUND_EXPR) |
| { |
| tree op1 = decay_conversion (TREE_OPERAND (exp, 1)); |
| return build (COMPOUND_EXPR, TREE_TYPE (op1), |
| TREE_OPERAND (exp, 0), op1); |
| } |
| |
| if (!lvalue_p (exp) |
| && ! (TREE_CODE (exp) == CONSTRUCTOR && TREE_STATIC (exp))) |
| { |
| error ("invalid use of non-lvalue array"); |
| return error_mark_node; |
| } |
| |
| constp = volatilep = 0; |
| if (TREE_CODE_CLASS (TREE_CODE (exp)) == 'r' |
| || TREE_CODE_CLASS (TREE_CODE (exp)) == 'd') |
| { |
| constp = TREE_READONLY (exp); |
| volatilep = TREE_THIS_VOLATILE (exp); |
| } |
| |
| restype = TREE_TYPE (type); |
| if (TYPE_READONLY (type) || TYPE_VOLATILE (type) |
| || constp || volatilep) |
| restype = cp_build_type_variant (restype, |
| TYPE_READONLY (type) || constp, |
| TYPE_VOLATILE (type) || volatilep); |
| ptrtype = build_pointer_type (restype); |
| |
| if (TREE_CODE (exp) == VAR_DECL) |
| { |
| /* ??? This is not really quite correct |
| in that the type of the operand of ADDR_EXPR |
| is not the target type of the type of the ADDR_EXPR itself. |
| Question is, can this lossage be avoided? */ |
| adr = build1 (ADDR_EXPR, ptrtype, exp); |
| if (mark_addressable (exp) == 0) |
| return error_mark_node; |
| TREE_CONSTANT (adr) = staticp (exp); |
| TREE_SIDE_EFFECTS (adr) = 0; /* Default would be, same as EXP. */ |
| return adr; |
| } |
| /* This way is better for a COMPONENT_REF since it can |
| simplify the offset for a component. */ |
| adr = build_unary_op (ADDR_EXPR, exp, 1); |
| return cp_convert (ptrtype, adr); |
| } |
| |
| return exp; |
| } |
| |
| tree |
| default_conversion (exp) |
| tree exp; |
| { |
| tree type; |
| enum tree_code code; |
| |
| exp = decay_conversion (exp); |
| |
| type = TREE_TYPE (exp); |
| code = TREE_CODE (type); |
| |
| if (INTEGRAL_CODE_P (code)) |
| { |
| tree t = type_promotes_to (type); |
| if (t != type) |
| return cp_convert (t, exp); |
| } |
| |
| return exp; |
| } |
| |
| /* Take the address of an inline function without setting TREE_ADDRESSABLE |
| or TREE_USED. */ |
| |
| tree |
| inline_conversion (exp) |
| tree exp; |
| { |
| if (TREE_CODE (exp) == FUNCTION_DECL) |
| { |
| tree type = build_type_variant |
| (TREE_TYPE (exp), TREE_READONLY (exp), TREE_THIS_VOLATILE (exp)); |
| exp = build1 (ADDR_EXPR, build_pointer_type (type), exp); |
| } |
| return exp; |
| } |
| |
| tree |
| build_object_ref (datum, basetype, field) |
| tree datum, basetype, field; |
| { |
| tree dtype; |
| if (datum == error_mark_node) |
| return error_mark_node; |
| |
| dtype = TREE_TYPE (datum); |
| if (TREE_CODE (dtype) == REFERENCE_TYPE) |
| dtype = TREE_TYPE (dtype); |
| if (! IS_AGGR_TYPE_CODE (TREE_CODE (dtype))) |
| { |
| cp_error ("request for member `%T::%D' in expression of non-aggregate type `%T'", |
| basetype, field, dtype); |
| return error_mark_node; |
| } |
| else if (IS_SIGNATURE (basetype)) |
| { |
| warning ("signature name in scope resolution ignored"); |
| return build_component_ref (datum, field, NULL_TREE, 1); |
| } |
| else if (is_aggr_type (basetype, 1)) |
| { |
| tree binfo = binfo_or_else (basetype, dtype); |
| if (binfo) |
| return build_x_component_ref (build_scoped_ref (datum, basetype), |
| field, binfo, 1); |
| } |
| return error_mark_node; |
| } |
| |
| /* Like `build_component_ref, but uses an already found field, and converts |
| from a reference. Must compute access for current_class_ref. |
| Otherwise, ok. */ |
| |
| tree |
| build_component_ref_1 (datum, field, protect) |
| tree datum, field; |
| int protect; |
| { |
| return convert_from_reference |
| (build_component_ref (datum, field, NULL_TREE, protect)); |
| } |
| |
| /* Given a COND_EXPR, MIN_EXPR, or MAX_EXPR in T, return it in a form that we |
| can, for example, use as an lvalue. This code used to be in |
| unary_complex_lvalue, but we needed it to deal with `a = (d == c) ? b : c' |
| expressions, where we're dealing with aggregates. But now it's again only |
| called from unary_complex_lvalue. The case (in particular) that led to |
| this was with CODE == ADDR_EXPR, since it's not an lvalue when we'd |
| get it there. */ |
| |
| static tree |
| rationalize_conditional_expr (code, t) |
| enum tree_code code; |
| tree t; |
| { |
| /* For MIN_EXPR or MAX_EXPR, fold-const.c has arranged things so that |
| the first operand is always the one to be used if both operands |
| are equal, so we know what conditional expression this used to be. */ |
| if (TREE_CODE (t) == MIN_EXPR || TREE_CODE (t) == MAX_EXPR) |
| { |
| return |
| build_conditional_expr (build_x_binary_op ((TREE_CODE (t) == MIN_EXPR |
| ? LE_EXPR : GE_EXPR), |
| TREE_OPERAND (t, 0), |
| TREE_OPERAND (t, 1)), |
| build_unary_op (code, TREE_OPERAND (t, 0), 0), |
| build_unary_op (code, TREE_OPERAND (t, 1), 0)); |
| } |
| |
| return |
| build_conditional_expr (TREE_OPERAND (t, 0), |
| build_unary_op (code, TREE_OPERAND (t, 1), 0), |
| build_unary_op (code, TREE_OPERAND (t, 2), 0)); |
| } |
| |
| /* Given the TYPE of an anonymous union field inside T, return the |
| FIELD_DECL for the field. If not found return NULL_TREE. Because |
| anonymous unions can nest, we must also search all anonymous unions |
| that are directly reachable. */ |
| |
| static tree |
| lookup_anon_field (t, type) |
| tree t, type; |
| { |
| tree field; |
| |
| for (field = TYPE_FIELDS (t); field; field = TREE_CHAIN (field)) |
| { |
| if (TREE_STATIC (field)) |
| continue; |
| if (TREE_CODE (field) != FIELD_DECL) |
| continue; |
| |
| /* If we find it directly, return the field. */ |
| if (DECL_NAME (field) == NULL_TREE |
| && type == TREE_TYPE (field)) |
| { |
| return field; |
| } |
| |
| /* Otherwise, it could be nested, search harder. */ |
| if (DECL_NAME (field) == NULL_TREE |
| && TREE_CODE (TREE_TYPE (field)) == UNION_TYPE) |
| { |
| tree subfield = lookup_anon_field (TREE_TYPE (field), type); |
| if (subfield) |
| return subfield; |
| } |
| } |
| return NULL_TREE; |
| } |
| |
| /* Build a COMPONENT_REF for a given DATUM, and it's member COMPONENT. |
| COMPONENT can be an IDENTIFIER_NODE that is the name of the member |
| that we are interested in, or it can be a FIELD_DECL. */ |
| |
| tree |
| build_component_ref (datum, component, basetype_path, protect) |
| tree datum, component, basetype_path; |
| int protect; |
| { |
| register tree basetype = TREE_TYPE (datum); |
| register enum tree_code code; |
| register tree field = NULL; |
| register tree ref; |
| |
| if (processing_template_decl) |
| return build_min_nt (COMPONENT_REF, datum, component); |
| |
| /* If DATUM is a COMPOUND_EXPR or COND_EXPR, move our reference |
| inside it. */ |
| switch (TREE_CODE (datum)) |
| { |
| case COMPOUND_EXPR: |
| { |
| tree value = build_component_ref (TREE_OPERAND (datum, 1), component, |
| basetype_path, protect); |
| return build (COMPOUND_EXPR, TREE_TYPE (value), |
| TREE_OPERAND (datum, 0), value); |
| } |
| case COND_EXPR: |
| return build_conditional_expr |
| (TREE_OPERAND (datum, 0), |
| build_component_ref (TREE_OPERAND (datum, 1), component, |
| basetype_path, protect), |
| build_component_ref (TREE_OPERAND (datum, 2), component, |
| basetype_path, protect)); |
| |
| case TEMPLATE_DECL: |
| cp_error ("invalid use of %D", datum); |
| datum = error_mark_node; |
| break; |
| |
| default: |
| break; |
| } |
| |
| code = TREE_CODE (basetype); |
| |
| if (code == REFERENCE_TYPE) |
| { |
| datum = convert_from_reference (datum); |
| basetype = TREE_TYPE (datum); |
| code = TREE_CODE (basetype); |
| } |
| if (TREE_CODE (datum) == OFFSET_REF) |
| { |
| datum = resolve_offset_ref (datum); |
| basetype = TREE_TYPE (datum); |
| code = TREE_CODE (basetype); |
| } |
| |
| /* First, see if there is a field or component with name COMPONENT. */ |
| if (TREE_CODE (component) == TREE_LIST) |
| { |
| /* I could not trigger this code. MvL */ |
| my_friendly_abort (980326); |
| #ifdef DEAD |
| my_friendly_assert (!(TREE_CHAIN (component) == NULL_TREE |
| && DECL_CHAIN (TREE_VALUE (component)) == NULL_TREE), 309); |
| #endif |
| return build (COMPONENT_REF, TREE_TYPE (component), datum, component); |
| } |
| |
| if (! IS_AGGR_TYPE_CODE (code)) |
| { |
| if (code != ERROR_MARK) |
| cp_error ("request for member `%D' in `%E', which is of non-aggregate type `%T'", |
| component, datum, basetype); |
| return error_mark_node; |
| } |
| |
| if (!complete_type_or_else (basetype)) |
| return error_mark_node; |
| |
| if (TREE_CODE (component) == BIT_NOT_EXPR) |
| { |
| if (TYPE_IDENTIFIER (basetype) != TREE_OPERAND (component, 0)) |
| { |
| cp_error ("destructor specifier `%T::~%T' must have matching names", |
| basetype, TREE_OPERAND (component, 0)); |
| return error_mark_node; |
| } |
| if (! TYPE_HAS_DESTRUCTOR (basetype)) |
| { |
| cp_error ("type `%T' has no destructor", basetype); |
| return error_mark_node; |
| } |
| return TREE_VEC_ELT (CLASSTYPE_METHOD_VEC (basetype), 1); |
| } |
| |
| /* Look up component name in the structure type definition. */ |
| if (CLASSTYPE_VFIELD (basetype) |
| && DECL_NAME (CLASSTYPE_VFIELD (basetype)) == component) |
| /* Special-case this because if we use normal lookups in an ambiguous |
| hierarchy, the compiler will abort (because vptr lookups are |
| not supposed to be ambiguous. */ |
| field = CLASSTYPE_VFIELD (basetype); |
| else if (TREE_CODE (component) == FIELD_DECL) |
| field = component; |
| else if (TREE_CODE (component) == TYPE_DECL) |
| { |
| cp_pedwarn ("invalid use of type decl `%#D' as expression", component); |
| return component; |
| } |
| else |
| { |
| tree name = component; |
| if (TREE_CODE (component) == VAR_DECL) |
| name = DECL_NAME (component); |
| if (basetype_path == NULL_TREE) |
| basetype_path = TYPE_BINFO (basetype); |
| field = lookup_field (basetype_path, name, |
| protect && !VFIELD_NAME_P (name), 0); |
| if (field == error_mark_node) |
| return error_mark_node; |
| |
| if (field == NULL_TREE) |
| { |
| /* Not found as a data field, look for it as a method. If found, |
| then if this is the only possible one, return it, else |
| report ambiguity error. */ |
| tree fndecls = lookup_fnfields (basetype_path, name, 1); |
| if (fndecls == error_mark_node) |
| return error_mark_node; |
| if (fndecls) |
| { |
| if (TREE_CHAIN (fndecls) == NULL_TREE |
| && TREE_CODE (TREE_VALUE (fndecls)) != OVERLOAD) |
| { |
| tree access, fndecl; |
| |
| /* Unique, so use this one now. */ |
| basetype = TREE_PURPOSE (fndecls); |
| fndecl = TREE_VALUE (fndecls); |
| access = compute_access (TREE_PURPOSE (fndecls), fndecl); |
| if (access == access_public_node) |
| { |
| if (DECL_VINDEX (fndecl) |
| && ! resolves_to_fixed_type_p (datum, 0)) |
| { |
| tree addr = build_unary_op (ADDR_EXPR, datum, 0); |
| tree fntype = TREE_TYPE (fndecl); |
| |
| addr = convert_pointer_to (DECL_CONTEXT (fndecl), |
| addr); |
| datum = build_indirect_ref (addr, NULL_PTR); |
| my_friendly_assert (datum != error_mark_node, 310); |
| fndecl = build_vfn_ref (&addr, datum, |
| DECL_VINDEX (fndecl)); |
| /* The type of fndecl is a function type, |
| not a pointer-to-function type, since |
| build_vfn_ref returns not the correct |
| vtable slot, but the indirection of the |
| correct vtable slot. */ |
| TREE_TYPE (fndecl) = fntype; |
| } |
| else |
| mark_used (fndecl); |
| return build (OFFSET_REF, TREE_TYPE (fndecl), |
| datum, fndecl); |
| } |
| if (access == access_protected_node) |
| cp_error ("member function `%D' is protected", fndecl); |
| else |
| cp_error ("member function `%D' is private", fndecl); |
| return error_mark_node; |
| } |
| else |
| { |
| /* Just act like build_offset_ref, since the object does |
| not matter unless we're actually calling the function. */ |
| tree t; |
| |
| t = build_tree_list (error_mark_node, fndecls); |
| TREE_TYPE (t) = build_offset_type (basetype, |
| unknown_type_node); |
| return t; |
| } |
| } |
| |
| cp_error ("`%#T' has no member named `%D'", basetype, name); |
| return error_mark_node; |
| } |
| else if (TREE_TYPE (field) == error_mark_node) |
| return error_mark_node; |
| |
| if (TREE_CODE (field) != FIELD_DECL) |
| { |
| if (TREE_CODE (field) == TYPE_DECL) |
| cp_pedwarn ("invalid use of type decl `%#D' as expression", field); |
| else if (DECL_RTL (field) != 0) |
| mark_used (field); |
| else |
| TREE_USED (field) = 1; |
| return field; |
| } |
| } |
| |
| /* See if we have to do any conversions so that we pick up the field from the |
| right context. */ |
| if (DECL_FIELD_CONTEXT (field) != basetype) |
| { |
| tree context = DECL_FIELD_CONTEXT (field); |
| tree base = context; |
| while (!comptypes (base, basetype,1) && TYPE_NAME (base) |
| && ANON_UNION_TYPE_P (base)) |
| { |
| base = TYPE_CONTEXT (base); |
| } |
| |
| /* Handle base classes here... */ |
| if (base != basetype && TYPE_USES_COMPLEX_INHERITANCE (basetype)) |
| { |
| tree addr = build_unary_op (ADDR_EXPR, datum, 0); |
| if (integer_zerop (addr)) |
| { |
| error ("invalid reference to NULL ptr, use ptr-to-member instead"); |
| return error_mark_node; |
| } |
| if (VBASE_NAME_P (DECL_NAME (field))) |
| { |
| /* It doesn't matter which vbase pointer we grab, just |
| find one of them. */ |
| tree binfo = get_binfo (base, |
| TREE_TYPE (TREE_TYPE (addr)), 0); |
| addr = convert_pointer_to_real (binfo, addr); |
| } |
| else |
| addr = convert_pointer_to (base, addr); |
| datum = build_indirect_ref (addr, NULL_PTR); |
| my_friendly_assert (datum != error_mark_node, 311); |
| } |
| basetype = base; |
| |
| /* Handle things from anon unions here... */ |
| if (TYPE_NAME (context) && ANON_UNION_TYPE_P (context)) |
| { |
| tree subfield = lookup_anon_field (basetype, context); |
| tree subdatum = build_component_ref (datum, subfield, |
| basetype_path, protect); |
| return build_component_ref (subdatum, field, basetype_path, protect); |
| } |
| } |
| |
| ref = fold (build (COMPONENT_REF, TREE_TYPE (field), |
| break_out_cleanups (datum), field)); |
| |
| if (TREE_READONLY (datum) || TREE_READONLY (field)) |
| TREE_READONLY (ref) = 1; |
| if (TREE_THIS_VOLATILE (datum) || TREE_THIS_VOLATILE (field)) |
| TREE_THIS_VOLATILE (ref) = 1; |
| if (DECL_LANG_SPECIFIC (field) && DECL_MUTABLE_P (field)) |
| TREE_READONLY (ref) = 0; |
| |
| return ref; |
| } |
| |
| /* Variant of build_component_ref for use in expressions, which should |
| never have REFERENCE_TYPE. */ |
| |
| tree |
| build_x_component_ref (datum, component, basetype_path, protect) |
| tree datum, component, basetype_path; |
| int protect; |
| { |
| tree t = build_component_ref (datum, component, basetype_path, protect); |
| |
| if (! processing_template_decl) |
| t = convert_from_reference (t); |
| |
| return t; |
| } |
| |
| /* Given an expression PTR for a pointer, return an expression |
| for the value pointed to. |
| ERRORSTRING is the name of the operator to appear in error messages. |
| |
| This function may need to overload OPERATOR_FNNAME. |
| Must also handle REFERENCE_TYPEs for C++. */ |
| |
| tree |
| build_x_indirect_ref (ptr, errorstring) |
| tree ptr; |
| char *errorstring; |
| { |
| tree rval; |
| |
| if (processing_template_decl) |
| return build_min_nt (INDIRECT_REF, ptr); |
| |
| rval = build_opfncall (INDIRECT_REF, LOOKUP_NORMAL, ptr, NULL_TREE, |
| NULL_TREE); |
| if (rval) |
| return rval; |
| return build_indirect_ref (ptr, errorstring); |
| } |
| |
| tree |
| build_indirect_ref (ptr, errorstring) |
| tree ptr; |
| char *errorstring; |
| { |
| register tree pointer, type; |
| |
| if (ptr == error_mark_node) |
| return error_mark_node; |
| |
| pointer = (TREE_CODE (TREE_TYPE (ptr)) == REFERENCE_TYPE |
| ? ptr : default_conversion (ptr)); |
| type = TREE_TYPE (pointer); |
| |
| if (ptr == current_class_ptr) |
| return current_class_ref; |
| |
| if (TREE_CODE (type) == POINTER_TYPE || TREE_CODE (type) == REFERENCE_TYPE) |
| { |
| if (TREE_CODE (pointer) == ADDR_EXPR |
| && !flag_volatile |
| && (TYPE_MAIN_VARIANT (TREE_TYPE (TREE_OPERAND (pointer, 0))) |
| == TYPE_MAIN_VARIANT (TREE_TYPE (type))) |
| && (TREE_READONLY (TREE_OPERAND (pointer, 0)) |
| == TYPE_READONLY (TREE_TYPE (type))) |
| && (TREE_THIS_VOLATILE (TREE_OPERAND (pointer, 0)) |
| == TYPE_VOLATILE (TREE_TYPE (type)))) |
| return TREE_OPERAND (pointer, 0); |
| else |
| { |
| tree t = TREE_TYPE (type); |
| register tree ref = build1 (INDIRECT_REF, |
| TYPE_MAIN_VARIANT (t), pointer); |
| |
| /* We *must* set TREE_READONLY when dereferencing a pointer to const, |
| so that we get the proper error message if the result is used |
| to assign to. Also, &* is supposed to be a no-op. */ |
| TREE_READONLY (ref) = TYPE_READONLY (t); |
| TREE_SIDE_EFFECTS (ref) |
| = (TYPE_VOLATILE (t) || TREE_SIDE_EFFECTS (pointer) |
| || flag_volatile); |
| TREE_THIS_VOLATILE (ref) = TYPE_VOLATILE (t); |
| return ref; |
| } |
| } |
| /* `pointer' won't be an error_mark_node if we were given a |
| pointer to member, so it's cool to check for this here. */ |
| else if (TYPE_PTRMEMFUNC_P (type)) |
| error ("invalid use of `%s' on pointer to member function", errorstring); |
| else if (TREE_CODE (type) == RECORD_TYPE |
| && (IS_SIGNATURE_POINTER (type) || IS_SIGNATURE_REFERENCE (type))) |
| error ("cannot dereference signature pointer/reference"); |
| else if (pointer != error_mark_node) |
| { |
| if (errorstring) |
| error ("invalid type argument of `%s'", errorstring); |
| else |
| error ("invalid type argument"); |
| } |
| return error_mark_node; |
| } |
| |
| /* This handles expressions of the form "a[i]", which denotes |
| an array reference. |
| |
| This is logically equivalent in C to *(a+i), but we may do it differently. |
| If A is a variable or a member, we generate a primitive ARRAY_REF. |
| This avoids forcing the array out of registers, and can work on |
| arrays that are not lvalues (for example, members of structures returned |
| by functions). |
| |
| If INDEX is of some user-defined type, it must be converted to |
| integer type. Otherwise, to make a compatible PLUS_EXPR, it |
| will inherit the type of the array, which will be some pointer type. */ |
| |
| tree |
| build_array_ref (array, idx) |
| tree array, idx; |
| { |
| if (idx == 0) |
| { |
| error ("subscript missing in array reference"); |
| return error_mark_node; |
| } |
| |
| if (TREE_TYPE (array) == error_mark_node |
| || TREE_TYPE (idx) == error_mark_node) |
| return error_mark_node; |
| |
| if (TREE_CODE (TREE_TYPE (array)) == ARRAY_TYPE |
| && TREE_CODE (array) != INDIRECT_REF) |
| { |
| tree rval, type; |
| |
| /* Subscripting with type char is likely to lose |
| on a machine where chars are signed. |
| So warn on any machine, but optionally. |
| Don't warn for unsigned char since that type is safe. |
| Don't warn for signed char because anyone who uses that |
| must have done so deliberately. */ |
| if (warn_char_subscripts |
| && TYPE_MAIN_VARIANT (TREE_TYPE (idx)) == char_type_node) |
| warning ("array subscript has type `char'"); |
| |
| /* Apply default promotions *after* noticing character types. */ |
| idx = default_conversion (idx); |
| |
| if (TREE_CODE (TREE_TYPE (idx)) != INTEGER_TYPE) |
| { |
| error ("array subscript is not an integer"); |
| return error_mark_node; |
| } |
| |
| /* An array that is indexed by a non-constant |
| cannot be stored in a register; we must be able to do |
| address arithmetic on its address. |
| Likewise an array of elements of variable size. */ |
| if (TREE_CODE (idx) != INTEGER_CST |
| || (TYPE_SIZE (TREE_TYPE (TREE_TYPE (array))) != 0 |
| && (TREE_CODE (TYPE_SIZE (TREE_TYPE (TREE_TYPE (array)))) |
| != INTEGER_CST))) |
| { |
| if (mark_addressable (array) == 0) |
| return error_mark_node; |
| } |
| /* An array that is indexed by a constant value which is not within |
| the array bounds cannot be stored in a register either; because we |
| would get a crash in store_bit_field/extract_bit_field when trying |
| to access a non-existent part of the register. */ |
| if (TREE_CODE (idx) == INTEGER_CST |
| && TYPE_VALUES (TREE_TYPE (array)) |
| && ! int_fits_type_p (idx, TYPE_VALUES (TREE_TYPE (array)))) |
| { |
| if (mark_addressable (array) == 0) |
| return error_mark_node; |
| } |
| |
| if (pedantic && !lvalue_p (array)) |
| pedwarn ("ANSI C++ forbids subscripting non-lvalue array"); |
| |
| /* Note in C++ it is valid to subscript a `register' array, since |
| it is valid to take the address of something with that |
| storage specification. */ |
| if (extra_warnings) |
| { |
| tree foo = array; |
| while (TREE_CODE (foo) == COMPONENT_REF) |
| foo = TREE_OPERAND (foo, 0); |
| if (TREE_CODE (foo) == VAR_DECL && DECL_REGISTER (foo)) |
| warning ("subscripting array declared `register'"); |
| } |
| |
| type = TYPE_MAIN_VARIANT (TREE_TYPE (TREE_TYPE (array))); |
| rval = build (ARRAY_REF, type, array, idx); |
| /* Array ref is const/volatile if the array elements are |
| or if the array is.. */ |
| TREE_READONLY (rval) |
| |= (TYPE_READONLY (TREE_TYPE (TREE_TYPE (array))) |
| | TREE_READONLY (array)); |
| TREE_SIDE_EFFECTS (rval) |
| |= (TYPE_VOLATILE (TREE_TYPE (TREE_TYPE (array))) |
| | TREE_SIDE_EFFECTS (array)); |
| TREE_THIS_VOLATILE (rval) |
| |= (TYPE_VOLATILE (TREE_TYPE (TREE_TYPE (array))) |
| /* This was added by rms on 16 Nov 91. |
| It fixes vol struct foo *a; a->elts[1] |
| in an inline function. |
| Hope it doesn't break something else. */ |
| | TREE_THIS_VOLATILE (array)); |
| return require_complete_type (fold (rval)); |
| } |
| |
| { |
| tree ar = default_conversion (array); |
| tree ind = default_conversion (idx); |
| |
| /* Put the integer in IND to simplify error checking. */ |
| if (TREE_CODE (TREE_TYPE (ar)) == INTEGER_TYPE) |
| { |
| tree temp = ar; |
| ar = ind; |
| ind = temp; |
| } |
| |
| if (ar == error_mark_node) |
| return ar; |
| |
| if (TREE_CODE (TREE_TYPE (ar)) != POINTER_TYPE) |
| { |
| error ("subscripted value is neither array nor pointer"); |
| return error_mark_node; |
| } |
| if (TREE_CODE (TREE_TYPE (ind)) != INTEGER_TYPE) |
| { |
| error ("array subscript is not an integer"); |
| return error_mark_node; |
| } |
| |
| return build_indirect_ref (build_binary_op_nodefault (PLUS_EXPR, ar, |
| ind, PLUS_EXPR), |
| "array indexing"); |
| } |
| } |
| |
| /* Build a function call to function FUNCTION with parameters PARAMS. |
| PARAMS is a list--a chain of TREE_LIST nodes--in which the |
| TREE_VALUE of each node is a parameter-expression. The PARAMS do |
| not include any object pointer that may be required. FUNCTION's |
| data type may be a function type or a pointer-to-function. |
| |
| For C++: If FUNCTION's data type is a TREE_LIST, then the tree list |
| is the list of possible methods that FUNCTION could conceivably |
| be. If the list of methods comes from a class, then it will be |
| a list of lists (where each element is associated with the class |
| that produced it), otherwise it will be a simple list (for |
| functions overloaded in global scope). |
| |
| In the first case, TREE_VALUE (function) is the head of one of those |
| lists, and TREE_PURPOSE is the name of the function. |
| |
| In the second case, TREE_PURPOSE (function) is the function's |
| name directly. |
| |
| DECL is the class instance variable, usually CURRENT_CLASS_REF. |
| |
| When calling a TEMPLATE_DECL, we don't require a complete return |
| type. */ |
| |
| tree |
| build_x_function_call (function, params, decl) |
| tree function, params, decl; |
| { |
| tree type; |
| tree template_id = NULL_TREE; |
| int is_method; |
| |
| if (function == error_mark_node) |
| return error_mark_node; |
| |
| if (processing_template_decl) |
| return build_min_nt (CALL_EXPR, function, params, NULL_TREE); |
| |
| /* Save explicit template arguments if found */ |
| if (TREE_CODE (function) == TEMPLATE_ID_EXPR) |
| { |
| template_id = function; |
| function = TREE_OPERAND (function, 0); |
| } |
| |
| type = TREE_TYPE (function); |
| |
| if (TREE_CODE (type) == OFFSET_TYPE |
| && TREE_TYPE (type) == unknown_type_node |
| && TREE_CODE (function) == TREE_LIST |
| && TREE_CHAIN (function) == NULL_TREE) |
| { |
| /* Undo (Foo:bar)()... */ |
| type = TYPE_OFFSET_BASETYPE (type); |
| function = TREE_VALUE (function); |
| my_friendly_assert (TREE_CODE (function) == TREE_LIST, 999); |
| my_friendly_assert (TREE_CHAIN (function) == NULL_TREE, 999); |
| function = TREE_VALUE (function); |
| if (TREE_CODE (function) == OVERLOAD) |
| function = OVL_FUNCTION (function); |
| my_friendly_assert (TREE_CODE (function) == FUNCTION_DECL, 999); |
| function = DECL_NAME (function); |
| return build_method_call (decl, function, params, |
| TYPE_BINFO (type), LOOKUP_NORMAL); |
| } |
| |
| is_method = ((TREE_CODE (function) == TREE_LIST |
| && current_class_type != NULL_TREE |
| && (IDENTIFIER_CLASS_VALUE (TREE_PURPOSE (function)) |
| == function)) |
| || TREE_CODE (function) == IDENTIFIER_NODE |
| || TREE_CODE (type) == METHOD_TYPE |
| || TYPE_PTRMEMFUNC_P (type)); |
| |
| if ((TREE_CODE (function) == FUNCTION_DECL |
| && DECL_STATIC_FUNCTION_P (function)) |
| || (TREE_CODE (function) == TEMPLATE_DECL |
| && DECL_STATIC_FUNCTION_P (DECL_RESULT (function)))) |
| return build_member_call |
| (DECL_CONTEXT (function), DECL_NAME (function), params); |
| |
| /* A friend template. Make it look like a toplevel declaration. */ |
| if (! is_method && TREE_CODE (function) == TEMPLATE_DECL) |
| function = scratch_ovl_cons (function, NULL_TREE); |
| |
| /* Handle methods, friends, and overloaded functions, respectively. */ |
| if (is_method) |
| { |
| tree basetype = NULL_TREE; |
| |
| if (TREE_CODE (function) == FUNCTION_DECL |
| || DECL_FUNCTION_TEMPLATE_P (function)) |
| { |
| basetype = DECL_CLASS_CONTEXT (function); |
| |
| if (DECL_NAME (function)) |
| function = DECL_NAME (function); |
| else |
| function = TYPE_IDENTIFIER (DECL_CLASS_CONTEXT (function)); |
| } |
| else if (TREE_CODE (function) == TREE_LIST) |
| { |
| my_friendly_assert (TREE_CODE (TREE_VALUE (function)) |
| == FUNCTION_DECL, 312); |
| basetype = DECL_CLASS_CONTEXT (TREE_VALUE (function)); |
| function = TREE_PURPOSE (function); |
| } |
| else if (TREE_CODE (function) != IDENTIFIER_NODE) |
| { |
| if (TREE_CODE (function) == OFFSET_REF) |
| { |
| if (TREE_OPERAND (function, 0)) |
| decl = TREE_OPERAND (function, 0); |
| } |
| /* Call via a pointer to member function. */ |
| if (decl == NULL_TREE) |
| { |
| error ("pointer to member function called, but not in class scope"); |
| return error_mark_node; |
| } |
| /* What other type of POINTER_TYPE could this be? */ |
| if (TREE_CODE (TREE_TYPE (function)) != POINTER_TYPE |
| && ! TYPE_PTRMEMFUNC_P (TREE_TYPE (function)) |
| && TREE_CODE (function) != OFFSET_REF) |
| function = build (OFFSET_REF, TREE_TYPE (type), NULL_TREE, |
| function); |
| goto do_x_function; |
| } |
| |
| /* this is an abbreviated method call. |
| must go through here in case it is a virtual function. |
| @@ Perhaps this could be optimized. */ |
| |
| if (basetype && (! current_class_type |
| || ! DERIVED_FROM_P (basetype, current_class_type))) |
| return build_member_call (basetype, function, params); |
| |
| if (decl == NULL_TREE) |
| { |
| if (current_class_type == NULL_TREE) |
| { |
| error ("object missing in call to method `%s'", |
| IDENTIFIER_POINTER (function)); |
| return error_mark_node; |
| } |
| /* Yow: call from a static member function. */ |
| decl = build1 (NOP_EXPR, build_pointer_type (current_class_type), |
| error_mark_node); |
| decl = build_indirect_ref (decl, NULL_PTR); |
| } |
| |
| /* Put back explicit template arguments, if any. */ |
| if (template_id) |
| function = template_id; |
| return build_method_call (decl, function, params, |
| NULL_TREE, LOOKUP_NORMAL); |
| } |
| else if (TREE_CODE (function) == COMPONENT_REF |
| && type == unknown_type_node) |
| { |
| /* Should we undo what was done in build_component_ref? */ |
| if (TREE_CODE (TREE_PURPOSE (TREE_OPERAND (function, 1))) == TREE_VEC) |
| /* Get the name that build_component_ref hid. */ |
| function = DECL_NAME (TREE_VALUE (TREE_OPERAND (function, 1))); |
| else |
| function = TREE_PURPOSE (TREE_OPERAND (function, 1)); |
| return build_method_call (decl, function, params, |
| NULL_TREE, LOOKUP_NORMAL); |
| } |
| else if (really_overloaded_fn (function)) |
| { |
| if (OVL_FUNCTION (function) == NULL_TREE) |
| { |
| cp_error ("function `%D' declared overloaded, but no definitions appear with which to resolve it?!?", |
| TREE_PURPOSE (function)); |
| return error_mark_node; |
| } |
| else |
| { |
| /* Put back explicit template arguments, if any. */ |
| if (template_id) |
| function = template_id; |
| return build_new_function_call (function, params); |
| } |
| } |
| else |
| /* Remove a potential OVERLOAD around it */ |
| function = OVL_CURRENT (function); |
| |
| do_x_function: |
| if (TREE_CODE (function) == OFFSET_REF) |
| { |
| /* If the component is a data element (or a virtual function), we play |
| games here to make things work. */ |
| tree decl_addr; |
| |
| if (TREE_OPERAND (function, 0)) |
| decl = TREE_OPERAND (function, 0); |
| else |
| decl = current_class_ref; |
| |
| decl_addr = build_unary_op (ADDR_EXPR, decl, 0); |
| |
| /* Sigh. OFFSET_REFs are being used for too many things. |
| They're being used both for -> and ->*, and we want to resolve |
| the -> cases here, but leave the ->*. We could use |
| resolve_offset_ref for those, too, but it would call |
| get_member_function_from_ptrfunc and decl_addr wouldn't get |
| updated properly. Nasty. */ |
| if (TREE_CODE (TREE_OPERAND (function, 1)) == FIELD_DECL) |
| function = resolve_offset_ref (function); |
| else |
| function = TREE_OPERAND (function, 1); |
| |
| function = get_member_function_from_ptrfunc (&decl_addr, function); |
| params = expr_tree_cons (NULL_TREE, decl_addr, params); |
| return build_function_call (function, params); |
| } |
| |
| type = TREE_TYPE (function); |
| if (type != error_mark_node) |
| { |
| if (TREE_CODE (type) == REFERENCE_TYPE) |
| type = TREE_TYPE (type); |
| |
| if (IS_AGGR_TYPE (type)) |
| return build_opfncall (CALL_EXPR, LOOKUP_NORMAL, function, params, NULL_TREE); |
| } |
| |
| if (is_method) |
| { |
| tree fntype = TREE_TYPE (function); |
| tree ctypeptr = NULL_TREE; |
| |
| /* Explicitly named method? */ |
| if (TREE_CODE (function) == FUNCTION_DECL) |
| ctypeptr = build_pointer_type (DECL_CLASS_CONTEXT (function)); |
| /* Expression with ptr-to-method type? It could either be a plain |
| usage, or it might be a case where the ptr-to-method is being |
| passed in as an argument. */ |
| else if (TYPE_PTRMEMFUNC_P (fntype)) |
| { |
| tree rec = TYPE_METHOD_BASETYPE (TREE_TYPE |
| (TYPE_PTRMEMFUNC_FN_TYPE (fntype))); |
| ctypeptr = build_pointer_type (rec); |
| } |
| /* Unexpected node type? */ |
| else |
| my_friendly_abort (116); |
| if (decl == NULL_TREE) |
| { |
| if (current_function_decl |
| && DECL_STATIC_FUNCTION_P (current_function_decl)) |
| error ("invalid call to member function needing `this' in static member function scope"); |
| else |
| error ("pointer to member function called, but not in class scope"); |
| return error_mark_node; |
| } |
| if (TREE_CODE (TREE_TYPE (decl)) != POINTER_TYPE |
| && ! TYPE_PTRMEMFUNC_P (TREE_TYPE (decl))) |
| { |
| decl = build_unary_op (ADDR_EXPR, decl, 0); |
| decl = convert_pointer_to (TREE_TYPE (ctypeptr), decl); |
| } |
| else |
| decl = build_c_cast (ctypeptr, decl); |
| params = expr_tree_cons (NULL_TREE, decl, params); |
| } |
| |
| return build_function_call (function, params); |
| } |
| |
| /* Resolve a pointer to member function. INSTANCE is the object |
| instance to use, if the member points to a virtual member. */ |
| |
| tree |
| get_member_function_from_ptrfunc (instance_ptrptr, function) |
| tree *instance_ptrptr; |
| tree function; |
| { |
| if (TREE_CODE (function) == OFFSET_REF) |
| { |
| function = TREE_OPERAND (function, 1); |
| } |
| |
| if (TYPE_PTRMEMFUNC_P (TREE_TYPE (function))) |
| { |
| tree fntype, idx, e1, delta, delta2, e2, e3, aref, vtbl; |
| tree instance; |
| |
| tree instance_ptr = *instance_ptrptr; |
| |
| if (TREE_SIDE_EFFECTS (instance_ptr)) |
| instance_ptr = save_expr (instance_ptr); |
| |
| if (TREE_SIDE_EFFECTS (function)) |
| function = save_expr (function); |
| |
| fntype = TYPE_PTRMEMFUNC_FN_TYPE (TREE_TYPE (function)); |
| |
| /* Promoting idx before saving it improves performance on RISC |
| targets. Without promoting, the first compare used |
| load-with-sign-extend, while the second used normal load then |
| shift to sign-extend. An optimizer flaw, perhaps, but it's easier |
| to make this change. */ |
| idx = save_expr (default_conversion |
| (build_component_ref (function, |
| index_identifier, |
| NULL_TREE, 0))); |
| e1 = build_binary_op (GT_EXPR, idx, integer_zero_node, 1); |
| delta = cp_convert (ptrdiff_type_node, |
| build_component_ref (function, delta_identifier, |
| NULL_TREE, 0)); |
| delta2 = DELTA2_FROM_PTRMEMFUNC (function); |
| |
| /* Convert down to the right base, before using the instance. */ |
| instance |
| = convert_pointer_to_real (TYPE_METHOD_BASETYPE (TREE_TYPE (fntype)), |
| instance_ptr); |
| if (instance == error_mark_node && instance_ptr != error_mark_node) |
| return instance; |
| |
| vtbl = convert_pointer_to (ptr_type_node, instance); |
| vtbl |
| = build (PLUS_EXPR, |
| build_pointer_type (build_pointer_type (vtable_entry_type)), |
| vtbl, cp_convert (ptrdiff_type_node, delta2)); |
| vtbl = build_indirect_ref (vtbl, NULL_PTR); |
| aref = build_array_ref (vtbl, build_binary_op (MINUS_EXPR, |
| idx, |
| integer_one_node, 1)); |
| if (! flag_vtable_thunks) |
| { |
| aref = save_expr (aref); |
| |
| delta = build_binary_op |
| (PLUS_EXPR, |
| build_conditional_expr (e1, build_component_ref (aref, |
| delta_identifier, |
| NULL_TREE, 0), |
| integer_zero_node), |
| delta, 1); |
| } |
| |
| *instance_ptrptr = build (PLUS_EXPR, TREE_TYPE (instance_ptr), |
| instance_ptr, delta); |
| if (flag_vtable_thunks) |
| e2 = aref; |
| else |
| e2 = build_component_ref (aref, pfn_identifier, NULL_TREE, 0); |
| |
| e3 = PFN_FROM_PTRMEMFUNC (function); |
| TREE_TYPE (e2) = TREE_TYPE (e3); |
| e1 = build_conditional_expr (e1, e2, e3); |
| |
| if (instance_ptr == error_mark_node |
| && TREE_CODE (e1) != ADDR_EXPR |
| && TREE_CODE (TREE_OPERAND (e1, 0)) != FUNCTION_DECL) |
| cp_error ("object missing in `%E'", function); |
| |
| function = e1; |
| |
| /* Make sure this doesn't get evaluated first inside one of the |
| branches of the COND_EXPR. */ |
| if (TREE_CODE (instance_ptr) == SAVE_EXPR) |
| function = build (COMPOUND_EXPR, TREE_TYPE (function), |
| instance_ptr, function); |
| } |
| return function; |
| } |
| |
| tree |
| build_function_call_real (function, params, require_complete, flags) |
| tree function, params; |
| int require_complete, flags; |
| { |
| register tree fntype, fndecl; |
| register tree value_type; |
| register tree coerced_params; |
| tree name = NULL_TREE, assembler_name = NULL_TREE; |
| int is_method; |
| |
| /* build_c_cast puts on a NOP_EXPR to make the result not an lvalue. |
| Strip such NOP_EXPRs, since FUNCTION is used in non-lvalue context. */ |
| if (TREE_CODE (function) == NOP_EXPR |
| && TREE_TYPE (function) == TREE_TYPE (TREE_OPERAND (function, 0))) |
| function = TREE_OPERAND (function, 0); |
| |
| if (TREE_CODE (function) == FUNCTION_DECL) |
| { |
| name = DECL_NAME (function); |
| assembler_name = DECL_ASSEMBLER_NAME (function); |
| |
| GNU_xref_call (current_function_decl, |
| IDENTIFIER_POINTER (name ? name |
| : TYPE_IDENTIFIER (DECL_CLASS_CONTEXT |
| (function)))); |
| mark_used (function); |
| fndecl = function; |
| |
| /* Convert anything with function type to a pointer-to-function. */ |
| if (pedantic && DECL_MAIN_P (function)) |
| pedwarn ("ANSI C++ forbids calling `main' from within program"); |
| |
| /* Differs from default_conversion by not setting TREE_ADDRESSABLE |
| (because calling an inline function does not mean the function |
| needs to be separately compiled). */ |
| |
| if (DECL_INLINE (function)) |
| function = inline_conversion (function); |
| else |
| function = build_addr_func (function); |
| } |
| else |
| { |
| fndecl = NULL_TREE; |
| |
| function = build_addr_func (function); |
| } |
| |
| if (function == error_mark_node) |
| return error_mark_node; |
| |
| fntype = TREE_TYPE (function); |
| |
| if (TYPE_PTRMEMFUNC_P (fntype)) |
| { |
| cp_error ("must use .* or ->* to call pointer-to-member function in `%E (...)'", |
| function); |
| return error_mark_node; |
| } |
| |
| is_method = (TREE_CODE (fntype) == POINTER_TYPE |
| && TREE_CODE (TREE_TYPE (fntype)) == METHOD_TYPE); |
| |
| if (!((TREE_CODE (fntype) == POINTER_TYPE |
| && TREE_CODE (TREE_TYPE (fntype)) == FUNCTION_TYPE) |
| || is_method |
| || TREE_CODE (function) == TEMPLATE_ID_EXPR)) |
| { |
| cp_error ("`%E' cannot be used as a function", function); |
| return error_mark_node; |
| } |
| |
| /* fntype now gets the type of function pointed to. */ |
| fntype = TREE_TYPE (fntype); |
| |
| /* Convert the parameters to the types declared in the |
| function prototype, or apply default promotions. */ |
| |
| if (flags & LOOKUP_COMPLAIN) |
| coerced_params = convert_arguments (NULL_TREE, TYPE_ARG_TYPES (fntype), |
| params, fndecl, LOOKUP_NORMAL); |
| else |
| coerced_params = convert_arguments (NULL_TREE, TYPE_ARG_TYPES (fntype), |
| params, fndecl, 0); |
| |
| if (coerced_params == error_mark_node) |
| { |
| if (flags & LOOKUP_SPECULATIVELY) |
| return NULL_TREE; |
| else |
| return error_mark_node; |
| } |
| |
| /* Check for errors in format strings. */ |
| |
| if (warn_format && (name || assembler_name)) |
| check_function_format (name, assembler_name, coerced_params); |
| |
| /* Recognize certain built-in functions so we can make tree-codes |
| other than CALL_EXPR. We do this when it enables fold-const.c |
| to do something useful. */ |
| |
| if (TREE_CODE (function) == ADDR_EXPR |
| && TREE_CODE (TREE_OPERAND (function, 0)) == FUNCTION_DECL |
| && DECL_BUILT_IN (TREE_OPERAND (function, 0))) |
| switch (DECL_FUNCTION_CODE (TREE_OPERAND (function, 0))) |
| { |
| case BUILT_IN_ABS: |
| case BUILT_IN_LABS: |
| case BUILT_IN_FABS: |
| if (coerced_params == 0) |
| return integer_zero_node; |
| return build_unary_op (ABS_EXPR, TREE_VALUE (coerced_params), 0); |
| |
| default: |
| break; |
| } |
| |
| /* C++ */ |
| value_type = TREE_TYPE (fntype) ? TREE_TYPE (fntype) : void_type_node; |
| { |
| register tree result |
| = build_call (function, value_type, coerced_params); |
| |
| if (require_complete) |
| { |
| if (value_type == void_type_node) |
| return result; |
| result = require_complete_type (result); |
| } |
| if (IS_AGGR_TYPE (value_type)) |
| result = build_cplus_new (value_type, result); |
| return convert_from_reference (result); |
| } |
| } |
| |
| tree |
| build_function_call (function, params) |
| tree function, params; |
| { |
| return build_function_call_real (function, params, 1, LOOKUP_NORMAL); |
| } |
| |
| /* Convert the actual parameter expressions in the list VALUES |
| to the types in the list TYPELIST. |
| If parmdecls is exhausted, or when an element has NULL as its type, |
| perform the default conversions. |
| |
| RETURN_LOC is the location of the return value, if known, NULL_TREE |
| otherwise. This is useful in the case where we can avoid creating |
| a temporary variable in the case where we can initialize the return |
| value directly. If we are not eliding constructors, then we set this |
| to NULL_TREE to avoid this avoidance. |
| |
| NAME is an IDENTIFIER_NODE or 0. It is used only for error messages. |
| |
| This is also where warnings about wrong number of args are generated. |
| |
| Return a list of expressions for the parameters as converted. |
| |
| Both VALUES and the returned value are chains of TREE_LIST nodes |
| with the elements of the list in the TREE_VALUE slots of those nodes. |
| |
| In C++, unspecified trailing parameters can be filled in with their |
| default arguments, if such were specified. Do so here. */ |
| |
| tree |
| convert_arguments (return_loc, typelist, values, fndecl, flags) |
| tree return_loc, typelist, values, fndecl; |
| int flags; |
| { |
| register tree typetail, valtail; |
| register tree result = NULL_TREE; |
| char *called_thing = 0; |
| int i = 0; |
| |
| if (! flag_elide_constructors) |
| return_loc = 0; |
| |
| /* Argument passing is always copy-initialization. */ |
| flags |= LOOKUP_ONLYCONVERTING; |
| |
| if (fndecl) |
| { |
| if (TREE_CODE (TREE_TYPE (fndecl)) == METHOD_TYPE) |
| { |
| if (DECL_NAME (fndecl) == NULL_TREE |
| || IDENTIFIER_HAS_TYPE_VALUE (DECL_NAME (fndecl))) |
| called_thing = "constructor"; |
| else |
| called_thing = "member function"; |
| } |
| else |
| called_thing = "function"; |
| } |
| |
| for (valtail = values, typetail = typelist; |
| valtail; |
| valtail = TREE_CHAIN (valtail), i++) |
| { |
| register tree type = typetail ? TREE_VALUE (typetail) : 0; |
| register tree val = TREE_VALUE (valtail); |
| |
| if (val == error_mark_node) |
| return error_mark_node; |
| |
| if (type == void_type_node) |
| { |
| if (fndecl) |
| { |
| cp_error_at ("too many arguments to %s `%+D'", called_thing, |
| fndecl); |
| error ("at this point in file"); |
| } |
| else |
| error ("too many arguments to function"); |
| /* In case anybody wants to know if this argument |
| list is valid. */ |
| if (result) |
| TREE_TYPE (tree_last (result)) = error_mark_node; |
| break; |
| } |
| |
| /* The tree type of the parameter being passed may not yet be |
| known. In this case, its type is TYPE_UNKNOWN, and will |
| be instantiated by the type given by TYPE. If TYPE |
| is also NULL, the tree type of VAL is ERROR_MARK_NODE. */ |
| if (type && type_unknown_p (val)) |
| val = require_instantiated_type (type, val, integer_zero_node); |
| else if (type_unknown_p (val)) |
| { |
| /* Strip the `&' from an overloaded FUNCTION_DECL. */ |
| if (TREE_CODE (val) == ADDR_EXPR) |
| val = TREE_OPERAND (val, 0); |
| if (really_overloaded_fn (val)) |
| cp_error ("insufficient type information to resolve address of overloaded function `%D'", |
| DECL_NAME (get_first_fn (val))); |
| else |
| error ("insufficient type information in parameter list"); |
| val = integer_zero_node; |
| } |
| else if (TREE_CODE (val) == OFFSET_REF |
| && TREE_CODE (TREE_TYPE (val)) == METHOD_TYPE) |
| { |
| /* This is unclean. Should be handled elsewhere. */ |
| val = build_unary_op (ADDR_EXPR, val, 0); |
| } |
| else if (TREE_CODE (val) == OFFSET_REF) |
| val = resolve_offset_ref (val); |
| |
| /* build_c_cast puts on a NOP_EXPR to make the result not an lvalue. |
| Strip such NOP_EXPRs, since VAL is used in non-lvalue context. */ |
| if (TREE_CODE (val) == NOP_EXPR |
| && TREE_TYPE (val) == TREE_TYPE (TREE_OPERAND (val, 0)) |
| && (type == 0 || TREE_CODE (type) != REFERENCE_TYPE)) |
| val = TREE_OPERAND (val, 0); |
| |
| if (type == 0 || TREE_CODE (type) != REFERENCE_TYPE) |
| { |
| if (TREE_CODE (TREE_TYPE (val)) == ARRAY_TYPE |
| || TREE_CODE (TREE_TYPE (val)) == FUNCTION_TYPE |
| || TREE_CODE (TREE_TYPE (val)) == METHOD_TYPE) |
| val = default_conversion (val); |
| |
| val = require_complete_type (val); |
| } |
| |
| if (val == error_mark_node) |
| return error_mark_node; |
| |
| if (type != 0) |
| { |
| /* Formal parm type is specified by a function prototype. */ |
| tree parmval; |
| |
| if (TYPE_SIZE (complete_type (type)) == 0) |
| { |
| error ("parameter type of called function is incomplete"); |
| parmval = val; |
| } |
| else |
| { |
| parmval = convert_for_initialization |
| (return_loc, type, val, flags, |
| "argument passing", fndecl, i); |
| #ifdef PROMOTE_PROTOTYPES |
| if ((TREE_CODE (type) == INTEGER_TYPE |
| || TREE_CODE (type) == ENUMERAL_TYPE) |
| && (TYPE_PRECISION (type) |
| < TYPE_PRECISION (integer_type_node))) |
| parmval = default_conversion (parmval); |
| #endif |
| } |
| |
| if (parmval == error_mark_node) |
| return error_mark_node; |
| |
| result = expr_tree_cons (NULL_TREE, parmval, result); |
| } |
| else |
| { |
| if (TREE_CODE (TREE_TYPE (val)) == REFERENCE_TYPE) |
| val = convert_from_reference (val); |
| |
| result = expr_tree_cons (NULL_TREE, |
| convert_arg_to_ellipsis (val), |
| result); |
| } |
| |
| if (typetail) |
| typetail = TREE_CHAIN (typetail); |
| } |
| |
| if (typetail != 0 && typetail != void_list_node) |
| { |
| /* See if there are default arguments that can be used */ |
| if (TREE_PURPOSE (typetail)) |
| { |
| for (; typetail != void_list_node; ++i) |
| { |
| tree type = TREE_VALUE (typetail); |
| tree val = TREE_PURPOSE (typetail); |
| tree parmval = convert_default_arg (type, val); |
| |
| if (parmval == error_mark_node) |
| return error_mark_node; |
| |
| result = expr_tree_cons (0, parmval, result); |
| typetail = TREE_CHAIN (typetail); |
| /* ends with `...'. */ |
| if (typetail == NULL_TREE) |
| break; |
| } |
| } |
| else |
| { |
| if (fndecl) |
| { |
| char *buf = (char *)alloca (32 + strlen (called_thing)); |
| sprintf (buf, "too few arguments to %s `%%#D'", called_thing); |
| cp_error_at (buf, fndecl); |
| error ("at this point in file"); |
| } |
| else |
| error ("too few arguments to function"); |
| return error_mark_list; |
| } |
| } |
| |
| return nreverse (result); |
| } |
| |
| /* Build a binary-operation expression, after performing default |
| conversions on the operands. CODE is the kind of expression to build. */ |
| |
| tree |
| build_x_binary_op (code, arg1, arg2) |
| enum tree_code code; |
| tree arg1, arg2; |
| |