| /* Language-level data type conversion for GNU C++. |
| Copyright (C) 1987-2022 Free Software Foundation, Inc. |
| Hacked by Michael Tiemann (tiemann@cygnus.com) |
| |
| This file is part of GCC. |
| |
| GCC 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 3, or (at your option) |
| any later version. |
| |
| GCC 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 GCC; see the file COPYING3. If not see |
| <http://www.gnu.org/licenses/>. */ |
| |
| |
| /* This file contains the functions for converting C++ expressions |
| to different data types. The only entry point is `convert'. |
| Every language front end must have a `convert' function |
| but what kind of conversions it does will depend on the language. */ |
| |
| #include "config.h" |
| #include "system.h" |
| #include "coretypes.h" |
| #include "target.h" |
| #include "cp-tree.h" |
| #include "stor-layout.h" |
| #include "flags.h" |
| #include "intl.h" |
| #include "convert.h" |
| #include "stringpool.h" |
| #include "attribs.h" |
| #include "escaped_string.h" |
| |
| static tree convert_to_pointer_force (tree, tree, tsubst_flags_t); |
| static tree build_type_conversion (tree, tree); |
| static tree build_up_reference (tree, tree, int, tree, tsubst_flags_t); |
| static void diagnose_ref_binding (location_t, tree, tree, tree); |
| |
| /* Change of width--truncation and extension of integers or reals-- |
| is represented with NOP_EXPR. Proper functioning of many things |
| assumes that no other conversions can be NOP_EXPRs. |
| |
| Conversion between integer and pointer is represented with CONVERT_EXPR. |
| Converting integer to real uses FLOAT_EXPR |
| and real to integer uses FIX_TRUNC_EXPR. |
| |
| Here is a list of all the functions that assume that widening and |
| narrowing is always done with a NOP_EXPR: |
| In convert.cc, convert_to_integer[_maybe_fold]. |
| In c-typeck.cc, build_binary_op_nodefault (boolean ops), |
| and c_common_truthvalue_conversion. |
| In expr.cc: expand_expr, for operands of a MULT_EXPR. |
| In fold-const.cc: fold. |
| In tree.cc: get_narrower and get_unwidened. |
| |
| C++: in multiple-inheritance, converting between pointers may involve |
| adjusting them by a delta stored within the class definition. */ |
| |
| /* Subroutines of `convert'. */ |
| |
| /* if converting pointer to pointer |
| if dealing with classes, check for derived->base or vice versa |
| else if dealing with method pointers, delegate |
| else convert blindly |
| else if converting class, pass off to build_type_conversion |
| else try C-style pointer conversion. */ |
| |
| static tree |
| cp_convert_to_pointer (tree type, tree expr, bool dofold, |
| tsubst_flags_t complain) |
| { |
| tree intype = TREE_TYPE (expr); |
| enum tree_code form; |
| tree rval; |
| location_t loc = cp_expr_loc_or_input_loc (expr); |
| |
| if (intype == error_mark_node) |
| return error_mark_node; |
| |
| if (MAYBE_CLASS_TYPE_P (intype)) |
| { |
| intype = complete_type (intype); |
| if (!COMPLETE_TYPE_P (intype)) |
| { |
| if (complain & tf_error) |
| error_at (loc, "cannot convert from incomplete type %qH to %qI", |
| intype, type); |
| return error_mark_node; |
| } |
| |
| rval = build_type_conversion (type, expr); |
| if (rval) |
| { |
| if ((complain & tf_error) |
| && rval == error_mark_node) |
| error_at (loc, "conversion of %qE from %qH to %qI is ambiguous", |
| expr, intype, type); |
| return rval; |
| } |
| } |
| |
| /* Handle anachronistic conversions from (::*)() to cv void* or (*)(). */ |
| if (TYPE_PTR_P (type) |
| && (TREE_CODE (TREE_TYPE (type)) == FUNCTION_TYPE |
| || VOID_TYPE_P (TREE_TYPE (type)))) |
| { |
| if (TYPE_PTRMEMFUNC_P (intype) |
| || TREE_CODE (intype) == METHOD_TYPE) |
| return convert_member_func_to_ptr (type, expr, complain); |
| if (TYPE_PTR_P (TREE_TYPE (expr))) |
| return build_nop (type, expr); |
| intype = TREE_TYPE (expr); |
| } |
| |
| if (expr == error_mark_node) |
| return error_mark_node; |
| |
| form = TREE_CODE (intype); |
| |
| if (INDIRECT_TYPE_P (intype)) |
| { |
| intype = TYPE_MAIN_VARIANT (intype); |
| |
| if (TYPE_MAIN_VARIANT (type) != intype |
| && TYPE_PTR_P (type) |
| && TREE_CODE (TREE_TYPE (type)) == RECORD_TYPE |
| && MAYBE_CLASS_TYPE_P (TREE_TYPE (type)) |
| && MAYBE_CLASS_TYPE_P (TREE_TYPE (intype)) |
| && TREE_CODE (TREE_TYPE (intype)) == RECORD_TYPE) |
| { |
| enum tree_code code = PLUS_EXPR; |
| tree binfo; |
| tree intype_class; |
| tree type_class; |
| bool same_p; |
| |
| intype_class = TREE_TYPE (intype); |
| type_class = TREE_TYPE (type); |
| |
| same_p = same_type_p (TYPE_MAIN_VARIANT (intype_class), |
| TYPE_MAIN_VARIANT (type_class)); |
| binfo = NULL_TREE; |
| /* Try derived to base conversion. */ |
| if (!same_p) |
| binfo = lookup_base (intype_class, type_class, ba_check, |
| NULL, complain); |
| if (!same_p && !binfo) |
| { |
| /* Try base to derived conversion. */ |
| binfo = lookup_base (type_class, intype_class, ba_check, |
| NULL, complain); |
| code = MINUS_EXPR; |
| } |
| if (binfo == error_mark_node) |
| return error_mark_node; |
| if (binfo || same_p) |
| { |
| if (binfo) |
| expr = build_base_path (code, expr, binfo, 0, complain); |
| /* Add any qualifier conversions. */ |
| return build_nop (type, expr); |
| } |
| } |
| |
| if (TYPE_PTRMEMFUNC_P (type)) |
| { |
| if (complain & tf_error) |
| error_at (loc, "cannot convert %qE from type %qH to type %qI", |
| expr, intype, type); |
| return error_mark_node; |
| } |
| |
| return build_nop (type, expr); |
| } |
| else if ((TYPE_PTRDATAMEM_P (type) && TYPE_PTRDATAMEM_P (intype)) |
| || (TYPE_PTRMEMFUNC_P (type) && TYPE_PTRMEMFUNC_P (intype))) |
| return convert_ptrmem (type, expr, /*allow_inverse_p=*/false, |
| /*c_cast_p=*/false, complain); |
| else if (TYPE_PTRMEMFUNC_P (intype)) |
| { |
| if (!warn_pmf2ptr) |
| { |
| if (TREE_CODE (expr) == PTRMEM_CST) |
| return cp_convert_to_pointer (type, PTRMEM_CST_MEMBER (expr), |
| dofold, complain); |
| else if (TREE_CODE (expr) == OFFSET_REF) |
| { |
| tree object = TREE_OPERAND (expr, 0); |
| return get_member_function_from_ptrfunc (&object, |
| TREE_OPERAND (expr, 1), |
| complain); |
| } |
| } |
| if (complain & tf_error) |
| error_at (loc, "cannot convert %qE from type %qH to type %qI", |
| expr, intype, type); |
| return error_mark_node; |
| } |
| |
| if (null_ptr_cst_p (expr)) |
| { |
| if (TYPE_PTRMEMFUNC_P (type)) |
| return build_ptrmemfunc (TYPE_PTRMEMFUNC_FN_TYPE (type), expr, 0, |
| /*c_cast_p=*/false, complain); |
| |
| if (complain & tf_warning) |
| maybe_warn_zero_as_null_pointer_constant (expr, loc); |
| |
| /* A NULL pointer-to-data-member is represented by -1, not by |
| zero. */ |
| tree val = (TYPE_PTRDATAMEM_P (type) |
| ? build_int_cst_type (type, -1) |
| : build_int_cst (type, 0)); |
| |
| return (TREE_SIDE_EFFECTS (expr) |
| ? build2 (COMPOUND_EXPR, type, expr, val) : val); |
| } |
| else if (TYPE_PTRMEM_P (type) && INTEGRAL_CODE_P (form)) |
| { |
| if (complain & tf_error) |
| error_at (loc, "invalid conversion from %qH to %qI", intype, type); |
| return error_mark_node; |
| } |
| |
| if (INTEGRAL_CODE_P (form)) |
| { |
| if (TYPE_PRECISION (intype) == POINTER_SIZE) |
| return build1 (CONVERT_EXPR, type, expr); |
| expr = cp_convert (c_common_type_for_size (TYPE_PRECISION (type), 0), expr, |
| complain); |
| /* Modes may be different but sizes should be the same. There |
| is supposed to be some integral type that is the same width |
| as a pointer. */ |
| gcc_assert (GET_MODE_SIZE (SCALAR_INT_TYPE_MODE (TREE_TYPE (expr))) |
| == GET_MODE_SIZE (SCALAR_INT_TYPE_MODE (type))); |
| |
| /* FIXME needed because convert_to_pointer_maybe_fold still folds |
| conversion of constants. */ |
| if (!dofold) |
| return build1 (CONVERT_EXPR, type, expr); |
| |
| return convert_to_pointer_maybe_fold (type, expr, dofold); |
| } |
| |
| if (type_unknown_p (expr)) |
| return instantiate_type (type, expr, complain); |
| |
| if (complain & tf_error) |
| error_at (loc, "cannot convert %qE from type %qH to type %qI", |
| expr, intype, type); |
| return error_mark_node; |
| } |
| |
| /* Like convert, except permit conversions to take place which |
| are not normally allowed due to access restrictions |
| (such as conversion from sub-type to private super-type). */ |
| |
| static tree |
| convert_to_pointer_force (tree type, tree expr, tsubst_flags_t complain) |
| { |
| tree intype = TREE_TYPE (expr); |
| enum tree_code form = TREE_CODE (intype); |
| |
| if (form == POINTER_TYPE) |
| { |
| intype = TYPE_MAIN_VARIANT (intype); |
| |
| if (TYPE_MAIN_VARIANT (type) != intype |
| && TREE_CODE (TREE_TYPE (type)) == RECORD_TYPE |
| && MAYBE_CLASS_TYPE_P (TREE_TYPE (type)) |
| && MAYBE_CLASS_TYPE_P (TREE_TYPE (intype)) |
| && TREE_CODE (TREE_TYPE (intype)) == RECORD_TYPE) |
| { |
| enum tree_code code = PLUS_EXPR; |
| tree binfo; |
| |
| binfo = lookup_base (TREE_TYPE (intype), TREE_TYPE (type), |
| ba_unique, NULL, complain); |
| if (!binfo) |
| { |
| binfo = lookup_base (TREE_TYPE (type), TREE_TYPE (intype), |
| ba_unique, NULL, complain); |
| code = MINUS_EXPR; |
| } |
| if (binfo == error_mark_node) |
| return error_mark_node; |
| if (binfo) |
| { |
| expr = build_base_path (code, expr, binfo, 0, complain); |
| if (expr == error_mark_node) |
| return error_mark_node; |
| /* Add any qualifier conversions. */ |
| if (!same_type_p (TREE_TYPE (TREE_TYPE (expr)), |
| TREE_TYPE (type))) |
| expr = build_nop (type, expr); |
| return expr; |
| } |
| } |
| } |
| |
| return cp_convert_to_pointer (type, expr, /*fold*/false, complain); |
| } |
| |
| /* We are passing something to a function which requires a reference. |
| The type we are interested in is in TYPE. The initial |
| value we have to begin with is in ARG. |
| |
| FLAGS controls how we manage access checking. |
| DIRECT_BIND in FLAGS controls how any temporaries are generated. |
| If DIRECT_BIND is set, DECL is the reference we're binding to. */ |
| |
| static tree |
| build_up_reference (tree type, tree arg, int flags, tree decl, |
| tsubst_flags_t complain) |
| { |
| tree rval; |
| tree argtype = TREE_TYPE (arg); |
| tree target_type = TREE_TYPE (type); |
| |
| gcc_assert (TYPE_REF_P (type)); |
| |
| if ((flags & DIRECT_BIND) && ! lvalue_p (arg)) |
| { |
| /* Create a new temporary variable. We can't just use a TARGET_EXPR |
| here because it needs to live as long as DECL. */ |
| tree targ = arg; |
| |
| arg = make_temporary_var_for_ref_to_temp (decl, target_type); |
| |
| /* Process the initializer for the declaration. */ |
| DECL_INITIAL (arg) = targ; |
| cp_finish_decl (arg, targ, /*init_const_expr_p=*/false, NULL_TREE, |
| LOOKUP_ONLYCONVERTING|DIRECT_BIND); |
| } |
| else if (!(flags & DIRECT_BIND) && ! obvalue_p (arg)) |
| return get_target_expr_sfinae (arg, complain); |
| |
| /* If we had a way to wrap this up, and say, if we ever needed its |
| address, transform all occurrences of the register, into a memory |
| reference we could win better. */ |
| rval = cp_build_addr_expr (arg, complain); |
| if (rval == error_mark_node) |
| return error_mark_node; |
| |
| if ((flags & LOOKUP_PROTECT) |
| && TYPE_MAIN_VARIANT (argtype) != TYPE_MAIN_VARIANT (target_type) |
| && MAYBE_CLASS_TYPE_P (argtype) |
| && MAYBE_CLASS_TYPE_P (target_type)) |
| { |
| /* We go through lookup_base for the access control. */ |
| tree binfo = lookup_base (argtype, target_type, ba_check, |
| NULL, complain); |
| if (binfo == error_mark_node) |
| return error_mark_node; |
| if (binfo == NULL_TREE) |
| return error_not_base_type (target_type, argtype); |
| rval = build_base_path (PLUS_EXPR, rval, binfo, 1, complain); |
| } |
| else |
| rval |
| = convert_to_pointer_force (build_pointer_type (target_type), |
| rval, complain); |
| return build_nop (type, rval); |
| } |
| |
| /* Subroutine of convert_to_reference. REFTYPE is the target reference type. |
| INTYPE is the original rvalue type and DECL is an optional _DECL node |
| for diagnostics. |
| |
| [dcl.init.ref] says that if an rvalue is used to |
| initialize a reference, then the reference must be to a |
| non-volatile const type. */ |
| |
| static void |
| diagnose_ref_binding (location_t loc, tree reftype, tree intype, tree decl) |
| { |
| tree ttl = TREE_TYPE (reftype); |
| |
| if (!TYPE_REF_IS_RVALUE (reftype) |
| && !CP_TYPE_CONST_NON_VOLATILE_P (ttl)) |
| { |
| const char *msg; |
| |
| if (CP_TYPE_VOLATILE_P (ttl) && decl) |
| msg = G_("initialization of volatile reference type %q#T from " |
| "rvalue of type %qT"); |
| else if (CP_TYPE_VOLATILE_P (ttl)) |
| msg = G_("conversion to volatile reference type %q#T " |
| "from rvalue of type %qT"); |
| else if (decl) |
| msg = G_("initialization of non-const reference type %q#T from " |
| "rvalue of type %qT"); |
| else |
| msg = G_("conversion to non-const reference type %q#T from " |
| "rvalue of type %qT"); |
| |
| permerror (loc, msg, reftype, intype); |
| } |
| } |
| |
| /* For C++: Only need to do one-level references, but cannot |
| get tripped up on signed/unsigned differences. |
| |
| DECL is either NULL_TREE or the _DECL node for a reference that is being |
| initialized. It can be error_mark_node if we don't know the _DECL but |
| we know it's an initialization. */ |
| |
| tree |
| convert_to_reference (tree reftype, tree expr, int convtype, |
| int flags, tree decl, tsubst_flags_t complain) |
| { |
| tree type = TYPE_MAIN_VARIANT (TREE_TYPE (reftype)); |
| tree intype; |
| tree rval = NULL_TREE; |
| tree rval_as_conversion = NULL_TREE; |
| bool can_convert_intype_to_type; |
| location_t loc = cp_expr_loc_or_input_loc (expr); |
| |
| if (TREE_CODE (type) == FUNCTION_TYPE |
| && TREE_TYPE (expr) == unknown_type_node) |
| expr = instantiate_type (type, expr, complain); |
| |
| if (expr == error_mark_node) |
| return error_mark_node; |
| |
| intype = TREE_TYPE (expr); |
| |
| gcc_assert (!TYPE_REF_P (intype)); |
| gcc_assert (TYPE_REF_P (reftype)); |
| |
| intype = TYPE_MAIN_VARIANT (intype); |
| |
| can_convert_intype_to_type = can_convert_standard (type, intype, complain); |
| |
| if (!can_convert_intype_to_type |
| && (convtype & CONV_IMPLICIT) && MAYBE_CLASS_TYPE_P (intype) |
| && ! (flags & LOOKUP_NO_CONVERSION)) |
| { |
| /* Look for a user-defined conversion to lvalue that we can use. */ |
| |
| rval_as_conversion |
| = build_type_conversion (reftype, expr); |
| |
| if (rval_as_conversion && rval_as_conversion != error_mark_node |
| && lvalue_p (rval_as_conversion)) |
| { |
| expr = rval_as_conversion; |
| rval_as_conversion = NULL_TREE; |
| intype = type; |
| can_convert_intype_to_type = 1; |
| } |
| } |
| |
| if (((convtype & CONV_STATIC) |
| && can_convert_standard (intype, type, complain)) |
| || ((convtype & CONV_IMPLICIT) && can_convert_intype_to_type)) |
| { |
| { |
| tree ttl = TREE_TYPE (reftype); |
| tree ttr = lvalue_type (expr); |
| |
| if ((complain & tf_error) |
| && ! lvalue_p (expr)) |
| diagnose_ref_binding (loc, reftype, intype, decl); |
| |
| if (! (convtype & CONV_CONST) |
| && !at_least_as_qualified_p (ttl, ttr)) |
| { |
| if (complain & tf_error) |
| permerror (loc, "conversion from %qH to %qI discards qualifiers", |
| ttr, reftype); |
| else |
| return error_mark_node; |
| } |
| } |
| |
| return build_up_reference (reftype, expr, flags, decl, complain); |
| } |
| else if ((convtype & CONV_REINTERPRET) && obvalue_p (expr)) |
| { |
| /* When casting an lvalue to a reference type, just convert into |
| a pointer to the new type and deference it. This is allowed |
| by San Diego WP section 5.2.9 paragraph 12, though perhaps it |
| should be done directly (jason). (int &)ri ---> *(int*)&ri */ |
| |
| /* B* bp; A& ar = (A&)bp; is valid, but it's probably not what they |
| meant. */ |
| if ((complain & tf_warning) |
| && TYPE_PTR_P (intype) |
| && (comptypes (TREE_TYPE (intype), type, |
| COMPARE_BASE | COMPARE_DERIVED))) |
| warning_at (loc, 0, "casting %qT to %qT does not dereference pointer", |
| intype, reftype); |
| |
| rval = cp_build_addr_expr (expr, complain); |
| if (rval != error_mark_node) |
| rval = convert_force (build_pointer_type (TREE_TYPE (reftype)), |
| rval, 0, complain); |
| if (rval != error_mark_node) |
| rval = build1 (NOP_EXPR, reftype, rval); |
| } |
| else |
| { |
| rval = convert_for_initialization (NULL_TREE, type, expr, flags, |
| ICR_CONVERTING, 0, 0, complain); |
| if (rval == NULL_TREE || rval == error_mark_node) |
| return rval; |
| if (complain & tf_error) |
| diagnose_ref_binding (loc, reftype, intype, decl); |
| rval = build_up_reference (reftype, rval, flags, decl, complain); |
| } |
| |
| if (rval) |
| { |
| /* If we found a way to convert earlier, then use it. */ |
| return rval; |
| } |
| |
| if (complain & tf_error) |
| error_at (loc, "cannot convert type %qH to type %qI", intype, reftype); |
| |
| return error_mark_node; |
| } |
| |
| /* We are using a reference VAL for its value. Bash that reference all the |
| way down to its lowest form. */ |
| |
| tree |
| convert_from_reference (tree val) |
| { |
| if (TREE_TYPE (val) |
| && TYPE_REF_P (TREE_TYPE (val))) |
| { |
| tree t = TREE_TYPE (TREE_TYPE (val)); |
| tree ref = build1 (INDIRECT_REF, t, val); |
| |
| mark_exp_read (val); |
| /* 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) = CP_TYPE_CONST_P (t); |
| TREE_THIS_VOLATILE (ref) = CP_TYPE_VOLATILE_P (t); |
| TREE_SIDE_EFFECTS (ref) |
| = (TREE_THIS_VOLATILE (ref) || TREE_SIDE_EFFECTS (val)); |
| val = ref; |
| } |
| |
| return val; |
| } |
| |
| /* Really perform an lvalue-to-rvalue conversion, including copying an |
| argument of class type into a temporary. */ |
| |
| tree |
| force_rvalue (tree expr, tsubst_flags_t complain) |
| { |
| tree type = TREE_TYPE (expr); |
| if (MAYBE_CLASS_TYPE_P (type) && TREE_CODE (expr) != TARGET_EXPR) |
| { |
| releasing_vec args (make_tree_vector_single (expr)); |
| expr = build_special_member_call (NULL_TREE, complete_ctor_identifier, |
| &args, type, LOOKUP_NORMAL, complain); |
| expr = build_cplus_new (type, expr, complain); |
| } |
| else |
| expr = decay_conversion (expr, complain); |
| |
| return expr; |
| } |
| |
| |
| /* If EXPR and ORIG are INTEGER_CSTs, return a version of EXPR that has |
| TREE_OVERFLOW set only if it is set in ORIG. Otherwise, return EXPR |
| unchanged. */ |
| |
| static tree |
| ignore_overflows (tree expr, tree orig) |
| { |
| tree stripped_expr = tree_strip_any_location_wrapper (expr); |
| tree stripped_orig = tree_strip_any_location_wrapper (orig); |
| |
| if (TREE_CODE (stripped_expr) == INTEGER_CST |
| && TREE_CODE (stripped_orig) == INTEGER_CST |
| && TREE_OVERFLOW (stripped_expr) != TREE_OVERFLOW (stripped_orig)) |
| { |
| gcc_assert (!TREE_OVERFLOW (stripped_orig)); |
| /* Ensure constant sharing. */ |
| stripped_expr = wide_int_to_tree (TREE_TYPE (stripped_expr), |
| wi::to_wide (stripped_expr)); |
| } |
| |
| return preserve_any_location_wrapper (stripped_expr, expr); |
| } |
| |
| /* Fold away simple conversions, but make sure TREE_OVERFLOW is set |
| properly and propagate TREE_NO_WARNING if folding EXPR results |
| in the same expression code. */ |
| |
| tree |
| cp_fold_convert (tree type, tree expr) |
| { |
| tree conv; |
| if (TREE_TYPE (expr) == type) |
| conv = expr; |
| else if (TREE_CODE (expr) == PTRMEM_CST |
| && same_type_p (TYPE_PTRMEM_CLASS_TYPE (type), |
| PTRMEM_CST_CLASS (expr))) |
| { |
| /* Avoid wrapping a PTRMEM_CST in NOP_EXPR. */ |
| conv = copy_node (expr); |
| TREE_TYPE (conv) = type; |
| } |
| else if (TYPE_PTRMEM_P (type)) |
| { |
| conv = convert_ptrmem (type, expr, true, false, |
| tf_warning_or_error); |
| conv = cp_fully_fold (conv); |
| } |
| else |
| { |
| conv = fold_convert (type, expr); |
| conv = ignore_overflows (conv, expr); |
| } |
| |
| if (TREE_CODE (expr) == TREE_CODE (conv)) |
| copy_warning (conv, expr); |
| |
| return conv; |
| } |
| |
| /* C++ conversions, preference to static cast conversions. */ |
| |
| tree |
| cp_convert (tree type, tree expr, tsubst_flags_t complain) |
| { |
| return ocp_convert (type, expr, CONV_OLD_CONVERT, LOOKUP_NORMAL, complain); |
| } |
| |
| /* C++ equivalent of convert_and_check but using cp_convert as the |
| conversion function. |
| |
| Convert EXPR to TYPE, warning about conversion problems with constants. |
| Invoke this function on every expression that is converted implicitly, |
| i.e. because of language rules and not because of an explicit cast. */ |
| |
| tree |
| cp_convert_and_check (tree type, tree expr, tsubst_flags_t complain) |
| { |
| tree result; |
| |
| if (TREE_TYPE (expr) == type) |
| return expr; |
| if (expr == error_mark_node) |
| return expr; |
| result = cp_convert (type, expr, complain); |
| |
| if ((complain & tf_warning) |
| && c_inhibit_evaluation_warnings == 0) |
| { |
| tree folded = cp_fully_fold (expr); |
| tree folded_result; |
| if (folded == expr) |
| folded_result = result; |
| else |
| { |
| /* Avoid bogus -Wparentheses warnings. */ |
| warning_sentinel w (warn_parentheses); |
| warning_sentinel c (warn_int_in_bool_context); |
| folded_result = cp_convert (type, folded, tf_none); |
| } |
| folded_result = fold_simple (folded_result); |
| if (!TREE_OVERFLOW_P (folded) |
| && folded_result != error_mark_node) |
| warnings_for_convert_and_check (cp_expr_loc_or_input_loc (expr), |
| type, folded, folded_result); |
| } |
| |
| return result; |
| } |
| |
| /* Conversion... |
| |
| FLAGS indicates how we should behave. */ |
| |
| tree |
| ocp_convert (tree type, tree expr, int convtype, int flags, |
| tsubst_flags_t complain) |
| { |
| tree e = expr; |
| enum tree_code code = TREE_CODE (type); |
| const char *invalid_conv_diag; |
| tree e1; |
| location_t loc = cp_expr_loc_or_input_loc (expr); |
| bool dofold = (convtype & CONV_FOLD); |
| |
| if (error_operand_p (e) || type == error_mark_node) |
| return error_mark_node; |
| |
| if (TREE_CODE (e) == COMPOUND_EXPR) |
| { |
| e = ocp_convert (type, TREE_OPERAND (e, 1), convtype, flags, complain); |
| if (e == error_mark_node) |
| return error_mark_node; |
| if (e == TREE_OPERAND (expr, 1)) |
| return expr; |
| return build2_loc (EXPR_LOCATION (expr), COMPOUND_EXPR, TREE_TYPE (e), |
| TREE_OPERAND (expr, 0), e); |
| } |
| |
| complete_type (type); |
| complete_type (TREE_TYPE (expr)); |
| |
| if ((invalid_conv_diag |
| = targetm.invalid_conversion (TREE_TYPE (expr), type))) |
| { |
| if (complain & tf_error) |
| error (invalid_conv_diag); |
| return error_mark_node; |
| } |
| |
| /* FIXME remove when moving to c_fully_fold model. */ |
| if (!CLASS_TYPE_P (type)) |
| { |
| e = mark_rvalue_use (e); |
| tree v = scalar_constant_value (e); |
| if (!error_operand_p (v)) |
| e = v; |
| } |
| if (error_operand_p (e)) |
| return error_mark_node; |
| |
| if (NULLPTR_TYPE_P (type) && null_ptr_cst_p (e)) |
| { |
| if (complain & tf_warning) |
| maybe_warn_zero_as_null_pointer_constant (e, loc); |
| |
| if (!TREE_SIDE_EFFECTS (e)) |
| return nullptr_node; |
| } |
| |
| if (MAYBE_CLASS_TYPE_P (type) && (convtype & CONV_FORCE_TEMP)) |
| /* We need a new temporary; don't take this shortcut. */; |
| else if (same_type_ignoring_top_level_qualifiers_p (type, TREE_TYPE (e))) |
| { |
| tree etype = TREE_TYPE (e); |
| if (same_type_p (type, etype)) |
| /* The call to fold will not always remove the NOP_EXPR as |
| might be expected, since if one of the types is a typedef; |
| the comparison in fold is just equality of pointers, not a |
| call to comptypes. We don't call fold in this case because |
| that can result in infinite recursion; fold will call |
| convert, which will call ocp_convert, etc. */ |
| return e; |
| /* For complex data types, we need to perform componentwise |
| conversion. */ |
| else if (TREE_CODE (type) == COMPLEX_TYPE) |
| return convert_to_complex_maybe_fold (type, e, dofold); |
| else if (VECTOR_TYPE_P (type)) |
| return convert_to_vector (type, rvalue (e)); |
| else if (TREE_CODE (e) == TARGET_EXPR) |
| { |
| /* Don't build a NOP_EXPR of class type. Instead, change the |
| type of the temporary. */ |
| gcc_assert (same_type_ignoring_top_level_qualifiers_p (type, etype)); |
| TREE_TYPE (e) = TREE_TYPE (TARGET_EXPR_SLOT (e)) = type; |
| return e; |
| } |
| else if (TREE_CODE (e) == CONSTRUCTOR) |
| { |
| gcc_assert (same_type_ignoring_top_level_qualifiers_p (type, etype)); |
| TREE_TYPE (e) = type; |
| return e; |
| } |
| else |
| { |
| /* We shouldn't be treating objects of ADDRESSABLE type as |
| rvalues. */ |
| gcc_assert (!TREE_ADDRESSABLE (type)); |
| return build_nop (type, e); |
| } |
| } |
| |
| e1 = targetm.convert_to_type (type, e); |
| if (e1) |
| return e1; |
| |
| if (code == VOID_TYPE && (convtype & CONV_STATIC)) |
| { |
| e = convert_to_void (e, ICV_CAST, complain); |
| return e; |
| } |
| |
| if (INTEGRAL_CODE_P (code)) |
| { |
| tree intype = TREE_TYPE (e); |
| tree converted; |
| |
| if (TREE_CODE (type) == ENUMERAL_TYPE) |
| { |
| /* enum = enum, enum = int, enum = float, (enum)pointer are all |
| errors. */ |
| if (((INTEGRAL_OR_ENUMERATION_TYPE_P (intype) |
| || TREE_CODE (intype) == REAL_TYPE) |
| && ! (convtype & CONV_STATIC)) |
| || TYPE_PTR_P (intype)) |
| { |
| if (complain & tf_error) |
| permerror (loc, "conversion from %q#T to %q#T", intype, type); |
| else |
| return error_mark_node; |
| } |
| |
| /* [expr.static.cast] |
| |
| 8. A value of integral or enumeration type can be explicitly |
| converted to an enumeration type. The value is unchanged if |
| the original value is within the range of the enumeration |
| values. Otherwise, the resulting enumeration value is |
| unspecified. */ |
| tree val = fold_for_warn (e); |
| if ((complain & tf_warning) |
| && TREE_CODE (val) == INTEGER_CST |
| && ENUM_UNDERLYING_TYPE (type) |
| && !int_fits_type_p (val, ENUM_UNDERLYING_TYPE (type))) |
| warning_at (loc, OPT_Wconversion, |
| "the result of the conversion is unspecified because " |
| "%qE is outside the range of type %qT", |
| expr, type); |
| } |
| if (MAYBE_CLASS_TYPE_P (intype)) |
| { |
| tree rval; |
| rval = build_type_conversion (type, e); |
| if (rval) |
| return rval; |
| if (complain & tf_error) |
| error_at (loc, "%q#T used where a %qT was expected", intype, type); |
| return error_mark_node; |
| } |
| if (code == BOOLEAN_TYPE) |
| { |
| if (VOID_TYPE_P (intype)) |
| { |
| if (complain & tf_error) |
| error_at (loc, |
| "could not convert %qE from %<void%> to %<bool%>", |
| expr); |
| return error_mark_node; |
| } |
| |
| if (VECTOR_TYPE_P (intype) && !gnu_vector_type_p (intype)) |
| { |
| if (complain & tf_error) |
| error_at (loc, "could not convert %qE from %qH to %qI", expr, |
| TREE_TYPE (expr), type); |
| return error_mark_node; |
| } |
| |
| /* We can't implicitly convert a scoped enum to bool, so convert |
| to the underlying type first. */ |
| if (SCOPED_ENUM_P (intype) && (convtype & CONV_STATIC)) |
| e = build_nop (ENUM_UNDERLYING_TYPE (intype), e); |
| if (complain & tf_warning) |
| return cp_truthvalue_conversion (e, complain); |
| else |
| { |
| /* Prevent bogus -Wint-in-bool-context warnings coming |
| from c_common_truthvalue_conversion down the line. */ |
| warning_sentinel w (warn_int_in_bool_context); |
| warning_sentinel c (warn_sign_compare); |
| return cp_truthvalue_conversion (e, complain); |
| } |
| } |
| |
| converted = convert_to_integer_maybe_fold (type, e, dofold); |
| |
| /* Ignore any integer overflow caused by the conversion. */ |
| return ignore_overflows (converted, e); |
| } |
| if (INDIRECT_TYPE_P (type) || TYPE_PTRMEM_P (type)) |
| return cp_convert_to_pointer (type, e, dofold, complain); |
| if (code == VECTOR_TYPE) |
| { |
| tree in_vtype = TREE_TYPE (e); |
| if (MAYBE_CLASS_TYPE_P (in_vtype)) |
| { |
| tree ret_val; |
| ret_val = build_type_conversion (type, e); |
| if (ret_val) |
| return ret_val; |
| if (complain & tf_error) |
| error_at (loc, "%q#T used where a %qT was expected", |
| in_vtype, type); |
| return error_mark_node; |
| } |
| return convert_to_vector (type, rvalue (e)); |
| } |
| if (code == REAL_TYPE || code == COMPLEX_TYPE) |
| { |
| if (MAYBE_CLASS_TYPE_P (TREE_TYPE (e))) |
| { |
| tree rval; |
| rval = build_type_conversion (type, e); |
| if (rval) |
| return rval; |
| else if (complain & tf_error) |
| error_at (loc, |
| "%q#T used where a floating-point value was expected", |
| TREE_TYPE (e)); |
| } |
| if (code == REAL_TYPE) |
| return convert_to_real_maybe_fold (type, e, dofold); |
| else if (code == COMPLEX_TYPE) |
| return convert_to_complex_maybe_fold (type, e, dofold); |
| } |
| |
| /* New C++ semantics: since assignment is now based on |
| memberwise copying, if the rhs type is derived from the |
| lhs type, then we may still do a conversion. */ |
| if (RECORD_OR_UNION_CODE_P (code)) |
| { |
| tree dtype = TREE_TYPE (e); |
| tree ctor = NULL_TREE; |
| |
| dtype = TYPE_MAIN_VARIANT (dtype); |
| |
| /* Conversion between aggregate types. New C++ semantics allow |
| objects of derived type to be cast to objects of base type. |
| Old semantics only allowed this between pointers. |
| |
| There may be some ambiguity between using a constructor |
| vs. using a type conversion operator when both apply. */ |
| |
| ctor = e; |
| |
| if (abstract_virtuals_error_sfinae (NULL_TREE, type, complain)) |
| return error_mark_node; |
| |
| if (BRACE_ENCLOSED_INITIALIZER_P (ctor)) |
| ctor = perform_implicit_conversion (type, ctor, complain); |
| else if ((flags & LOOKUP_ONLYCONVERTING) |
| && ! (CLASS_TYPE_P (dtype) && DERIVED_FROM_P (type, dtype))) |
| /* For copy-initialization, first we create a temp of the proper type |
| with a user-defined conversion sequence, then we direct-initialize |
| the target with the temp (see [dcl.init]). */ |
| ctor = build_user_type_conversion (type, ctor, flags, complain); |
| else |
| { |
| releasing_vec ctor_vec (make_tree_vector_single (ctor)); |
| ctor = build_special_member_call (NULL_TREE, |
| complete_ctor_identifier, |
| &ctor_vec, |
| type, flags, complain); |
| } |
| if (ctor) |
| return build_cplus_new (type, ctor, complain); |
| } |
| |
| if (complain & tf_error) |
| { |
| /* If the conversion failed and expr was an invalid use of pointer to |
| member function, try to report a meaningful error. */ |
| if (invalid_nonstatic_memfn_p (loc, expr, complain)) |
| /* We displayed the error message. */; |
| else |
| error_at (loc, "conversion from %qH to non-scalar type %qI requested", |
| TREE_TYPE (expr), type); |
| } |
| return error_mark_node; |
| } |
| |
| /* If CALL is a call, return the callee; otherwise null. */ |
| |
| tree |
| cp_get_callee (tree call) |
| { |
| if (call == NULL_TREE) |
| return call; |
| else if (TREE_CODE (call) == CALL_EXPR) |
| return CALL_EXPR_FN (call); |
| else if (TREE_CODE (call) == AGGR_INIT_EXPR) |
| return AGGR_INIT_EXPR_FN (call); |
| return NULL_TREE; |
| } |
| |
| /* FN is the callee of a CALL_EXPR or AGGR_INIT_EXPR; return the FUNCTION_DECL |
| if we can. */ |
| |
| tree |
| cp_get_fndecl_from_callee (tree fn, bool fold /* = true */) |
| { |
| if (fn == NULL_TREE) |
| return fn; |
| if (TREE_CODE (fn) == FUNCTION_DECL) |
| return fn; |
| tree type = TREE_TYPE (fn); |
| if (type == NULL_TREE || !INDIRECT_TYPE_P (type)) |
| return NULL_TREE; |
| if (fold) |
| fn = maybe_constant_init (fn); |
| STRIP_NOPS (fn); |
| if (TREE_CODE (fn) == ADDR_EXPR |
| || TREE_CODE (fn) == FDESC_EXPR) |
| fn = TREE_OPERAND (fn, 0); |
| if (TREE_CODE (fn) == FUNCTION_DECL) |
| return fn; |
| return NULL_TREE; |
| } |
| |
| /* Like get_callee_fndecl, but handles AGGR_INIT_EXPR as well and uses the |
| constexpr machinery. */ |
| |
| tree |
| cp_get_callee_fndecl (tree call) |
| { |
| return cp_get_fndecl_from_callee (cp_get_callee (call)); |
| } |
| |
| /* As above, but not using the constexpr machinery. */ |
| |
| tree |
| cp_get_callee_fndecl_nofold (tree call) |
| { |
| return cp_get_fndecl_from_callee (cp_get_callee (call), false); |
| } |
| |
| /* Subroutine of convert_to_void. Warn if we're discarding something with |
| attribute [[nodiscard]]. */ |
| |
| static void |
| maybe_warn_nodiscard (tree expr, impl_conv_void implicit) |
| { |
| if (!warn_unused_result || c_inhibit_evaluation_warnings) |
| return; |
| |
| tree call = expr; |
| if (TREE_CODE (expr) == TARGET_EXPR) |
| call = TARGET_EXPR_INITIAL (expr); |
| location_t loc = cp_expr_loc_or_input_loc (call); |
| tree callee = cp_get_callee (call); |
| if (!callee) |
| return; |
| |
| tree type = TREE_TYPE (callee); |
| if (TYPE_PTRMEMFUNC_P (type)) |
| type = TYPE_PTRMEMFUNC_FN_TYPE (type); |
| if (INDIRECT_TYPE_P (type)) |
| type = TREE_TYPE (type); |
| |
| tree rettype = TREE_TYPE (type); |
| tree fn = cp_get_fndecl_from_callee (callee); |
| tree attr; |
| if (implicit != ICV_CAST && fn |
| && (attr = lookup_attribute ("nodiscard", DECL_ATTRIBUTES (fn)))) |
| { |
| escaped_string msg; |
| tree args = TREE_VALUE (attr); |
| if (args) |
| msg.escape (TREE_STRING_POINTER (TREE_VALUE (args))); |
| const char *format |
| = (msg |
| ? G_("ignoring return value of %qD, " |
| "declared with attribute %<nodiscard%>: %<%s%>") |
| : G_("ignoring return value of %qD, " |
| "declared with attribute %<nodiscard%>%s")); |
| const char *raw_msg = msg ? (const char *) msg : ""; |
| auto_diagnostic_group d; |
| if (warning_at (loc, OPT_Wunused_result, format, fn, raw_msg)) |
| inform (DECL_SOURCE_LOCATION (fn), "declared here"); |
| } |
| else if (implicit != ICV_CAST |
| && (attr = lookup_attribute ("nodiscard", TYPE_ATTRIBUTES (rettype)))) |
| { |
| escaped_string msg; |
| tree args = TREE_VALUE (attr); |
| if (args) |
| msg.escape (TREE_STRING_POINTER (TREE_VALUE (args))); |
| const char *format |
| = (msg |
| ? G_("ignoring returned value of type %qT, " |
| "declared with attribute %<nodiscard%>: %<%s%>") |
| : G_("ignoring returned value of type %qT, " |
| "declared with attribute %<nodiscard%>%s")); |
| const char *raw_msg = msg ? (const char *) msg : ""; |
| auto_diagnostic_group d; |
| if (warning_at (loc, OPT_Wunused_result, format, rettype, raw_msg)) |
| { |
| if (fn) |
| inform (DECL_SOURCE_LOCATION (fn), |
| "in call to %qD, declared here", fn); |
| inform (DECL_SOURCE_LOCATION (TYPE_NAME (rettype)), |
| "%qT declared here", rettype); |
| } |
| } |
| else if (TREE_CODE (expr) == TARGET_EXPR |
| && lookup_attribute ("warn_unused_result", TYPE_ATTRIBUTES (type))) |
| { |
| /* The TARGET_EXPR confuses do_warn_unused_result into thinking that the |
| result is used, so handle that case here. */ |
| if (fn) |
| { |
| auto_diagnostic_group d; |
| if (warning_at (loc, OPT_Wunused_result, |
| "ignoring return value of %qD, " |
| "declared with attribute %<warn_unused_result%>", |
| fn)) |
| inform (DECL_SOURCE_LOCATION (fn), "declared here"); |
| } |
| else |
| warning_at (loc, OPT_Wunused_result, |
| "ignoring return value of function " |
| "declared with attribute %<warn_unused_result%>"); |
| } |
| } |
| |
| /* When an expression is used in a void context, its value is discarded and |
| no lvalue-rvalue and similar conversions happen [expr.static.cast/4, |
| stmt.expr/1, expr.comma/1]. This permits dereferencing an incomplete type |
| in a void context. The C++ standard does not define what an `access' to an |
| object is, but there is reason to believe that it is the lvalue to rvalue |
| conversion -- if it were not, `*&*p = 1' would violate [expr]/4 in that it |
| accesses `*p' not to calculate the value to be stored. But, dcl.type.cv/8 |
| indicates that volatile semantics should be the same between C and C++ |
| where ever possible. C leaves it implementation defined as to what |
| constitutes an access to a volatile. So, we interpret `*vp' as a read of |
| the volatile object `vp' points to, unless that is an incomplete type. For |
| volatile references we do not do this interpretation, because that would |
| make it impossible to ignore the reference return value from functions. We |
| issue warnings in the confusing cases. |
| |
| The IMPLICIT is ICV_CAST when the user is explicitly converting an expression |
| to void via a cast. If an expression is being implicitly converted, IMPLICIT |
| indicates the context of the implicit conversion. */ |
| |
| tree |
| convert_to_void (tree expr, impl_conv_void implicit, tsubst_flags_t complain) |
| { |
| location_t loc = cp_expr_loc_or_input_loc (expr); |
| |
| if (expr == error_mark_node |
| || TREE_TYPE (expr) == error_mark_node) |
| return error_mark_node; |
| |
| expr = maybe_undo_parenthesized_ref (expr); |
| |
| expr = mark_discarded_use (expr); |
| if (implicit == ICV_CAST) |
| /* An explicit cast to void avoids all -Wunused-but-set* warnings. */ |
| mark_exp_read (expr); |
| |
| if (!TREE_TYPE (expr)) |
| return expr; |
| if (invalid_nonstatic_memfn_p (loc, expr, complain)) |
| return error_mark_node; |
| if (TREE_CODE (expr) == PSEUDO_DTOR_EXPR) |
| { |
| if (complain & tf_error) |
| error_at (loc, "pseudo-destructor is not called"); |
| return error_mark_node; |
| } |
| |
| /* Explicitly evaluate void-converted concept checks since their |
| satisfaction may produce ill-formed programs. */ |
| if (concept_check_p (expr)) |
| expr = evaluate_concept_check (expr); |
| |
| if (VOID_TYPE_P (TREE_TYPE (expr))) |
| return expr; |
| switch (TREE_CODE (expr)) |
| { |
| case COND_EXPR: |
| { |
| /* The two parts of a cond expr might be separate lvalues. */ |
| tree op1 = TREE_OPERAND (expr,1); |
| tree op2 = TREE_OPERAND (expr,2); |
| bool side_effects = ((op1 && TREE_SIDE_EFFECTS (op1)) |
| || TREE_SIDE_EFFECTS (op2)); |
| tree new_op1, new_op2; |
| new_op1 = NULL_TREE; |
| if (implicit != ICV_CAST && !side_effects) |
| { |
| if (op1) |
| new_op1 = convert_to_void (op1, ICV_SECOND_OF_COND, complain); |
| new_op2 = convert_to_void (op2, ICV_THIRD_OF_COND, complain); |
| } |
| else |
| { |
| if (op1) |
| new_op1 = convert_to_void (op1, ICV_CAST, complain); |
| new_op2 = convert_to_void (op2, ICV_CAST, complain); |
| } |
| |
| expr = build3_loc (loc, COND_EXPR, TREE_TYPE (new_op2), |
| TREE_OPERAND (expr, 0), new_op1, new_op2); |
| break; |
| } |
| |
| case COMPOUND_EXPR: |
| { |
| /* The second part of a compound expr contains the value. */ |
| tree op1 = TREE_OPERAND (expr,1); |
| tree new_op1; |
| if (implicit != ICV_CAST && !warning_suppressed_p (expr /* What warning? */)) |
| new_op1 = convert_to_void (op1, ICV_RIGHT_OF_COMMA, complain); |
| else |
| new_op1 = convert_to_void (op1, ICV_CAST, complain); |
| |
| if (new_op1 != op1) |
| { |
| tree t = build2_loc (loc, COMPOUND_EXPR, TREE_TYPE (new_op1), |
| TREE_OPERAND (expr, 0), new_op1); |
| expr = t; |
| } |
| |
| break; |
| } |
| |
| case NON_LVALUE_EXPR: |
| case NOP_EXPR: |
| /* These have already decayed to rvalue. */ |
| break; |
| |
| case CALL_EXPR: /* We have a special meaning for volatile void fn(). */ |
| /* cdtors may return this or void, depending on |
| targetm.cxx.cdtor_returns_this, but this shouldn't affect our |
| decisions here: neither nodiscard warnings (nodiscard dtors |
| are nonsensical and ctors have a different behavior with that |
| attribute that is handled in the TARGET_EXPR case), nor should |
| any constexpr or template instantiations be affected by an ABI |
| property that is, or at least ought to be transparent to the |
| language. */ |
| if (tree fn = cp_get_callee_fndecl_nofold (expr)) |
| if (DECL_CONSTRUCTOR_P (fn) || DECL_DESTRUCTOR_P (fn)) |
| return expr; |
| |
| if (complain & tf_warning) |
| maybe_warn_nodiscard (expr, implicit); |
| break; |
| |
| case INDIRECT_REF: |
| { |
| tree type = TREE_TYPE (expr); |
| int is_reference = TYPE_REF_P (TREE_TYPE (TREE_OPERAND (expr, 0))); |
| int is_volatile = TYPE_VOLATILE (type); |
| int is_complete = COMPLETE_TYPE_P (complete_type (type)); |
| |
| /* Can't load the value if we don't know the type. */ |
| if (is_volatile && !is_complete) |
| { |
| if (complain & tf_warning) |
| switch (implicit) |
| { |
| case ICV_CAST: |
| warning_at (loc, 0, "conversion to void will not access " |
| "object of incomplete type %qT", type); |
| break; |
| case ICV_SECOND_OF_COND: |
| warning_at (loc, 0, "indirection will not access object of " |
| "incomplete type %qT in second operand " |
| "of conditional expression", type); |
| break; |
| case ICV_THIRD_OF_COND: |
| warning_at (loc, 0, "indirection will not access object of " |
| "incomplete type %qT in third operand " |
| "of conditional expression", type); |
| break; |
| case ICV_RIGHT_OF_COMMA: |
| warning_at (loc, 0, "indirection will not access object of " |
| "incomplete type %qT in right operand of " |
| "comma operator", type); |
| break; |
| case ICV_LEFT_OF_COMMA: |
| warning_at (loc, 0, "indirection will not access object of " |
| "incomplete type %qT in left operand of " |
| "comma operator", type); |
| break; |
| case ICV_STATEMENT: |
| warning_at (loc, 0, "indirection will not access object of " |
| "incomplete type %qT in statement", type); |
| break; |
| case ICV_THIRD_IN_FOR: |
| warning_at (loc, 0, "indirection will not access object of " |
| "incomplete type %qT in for increment " |
| "expression", type); |
| break; |
| default: |
| gcc_unreachable (); |
| } |
| } |
| /* Don't load the value if this is an implicit dereference, or if |
| the type needs to be handled by ctors/dtors. */ |
| else if (is_volatile && is_reference) |
| { |
| if (complain & tf_warning) |
| switch (implicit) |
| { |
| case ICV_CAST: |
| warning_at (loc, 0, "conversion to void will not access " |
| "object of type %qT", type); |
| break; |
| case ICV_SECOND_OF_COND: |
| warning_at (loc, 0, "implicit dereference will not access " |
| "object of type %qT in second operand of " |
| "conditional expression", type); |
| break; |
| case ICV_THIRD_OF_COND: |
| warning_at (loc, 0, "implicit dereference will not access " |
| "object of type %qT in third operand of " |
| "conditional expression", type); |
| break; |
| case ICV_RIGHT_OF_COMMA: |
| warning_at (loc, 0, "implicit dereference will not access " |
| "object of type %qT in right operand of " |
| "comma operator", type); |
| break; |
| case ICV_LEFT_OF_COMMA: |
| warning_at (loc, 0, "implicit dereference will not access " |
| "object of type %qT in left operand of comma " |
| "operator", type); |
| break; |
| case ICV_STATEMENT: |
| warning_at (loc, 0, "implicit dereference will not access " |
| "object of type %qT in statement", type); |
| break; |
| case ICV_THIRD_IN_FOR: |
| warning_at (loc, 0, "implicit dereference will not access " |
| "object of type %qT in for increment expression", |
| type); |
| break; |
| default: |
| gcc_unreachable (); |
| } |
| } |
| else if (is_volatile && TREE_ADDRESSABLE (type)) |
| { |
| if (complain & tf_warning) |
| switch (implicit) |
| { |
| case ICV_CAST: |
| warning_at (loc, 0, "conversion to void will not access " |
| "object of non-trivially-copyable type %qT", |
| type); |
| break; |
| case ICV_SECOND_OF_COND: |
| warning_at (loc, 0, "indirection will not access object of " |
| "non-trivially-copyable type %qT in second " |
| "operand of conditional expression", type); |
| break; |
| case ICV_THIRD_OF_COND: |
| warning_at (loc, 0, "indirection will not access object of " |
| "non-trivially-copyable type %qT in third " |
| "operand of conditional expression", type); |
| break; |
| case ICV_RIGHT_OF_COMMA: |
| warning_at (loc, 0, "indirection will not access object of " |
| "non-trivially-copyable type %qT in right " |
| "operand of comma operator", type); |
| break; |
| case ICV_LEFT_OF_COMMA: |
| warning_at (loc, 0, "indirection will not access object of " |
| "non-trivially-copyable type %qT in left " |
| "operand of comma operator", type); |
| break; |
| case ICV_STATEMENT: |
| warning_at (loc, 0, "indirection will not access object of " |
| "non-trivially-copyable type %qT in statement", |
| type); |
| break; |
| case ICV_THIRD_IN_FOR: |
| warning_at (loc, 0, "indirection will not access object of " |
| "non-trivially-copyable type %qT in for " |
| "increment expression", type); |
| break; |
| default: |
| gcc_unreachable (); |
| } |
| } |
| if (is_reference || !is_volatile || !is_complete || TREE_ADDRESSABLE (type)) |
| { |
| /* Emit a warning (if enabled) when the "effect-less" INDIRECT_REF |
| operation is stripped off. Note that we don't warn about |
| - an expression with TREE_NO_WARNING set. (For an example of |
| such expressions, see build_over_call in call.cc.) |
| - automatic dereferencing of references, since the user cannot |
| control it. (See also warn_if_unused_value() in c-common.cc.) */ |
| if (warn_unused_value |
| && implicit != ICV_CAST |
| && (complain & tf_warning) |
| && !warning_suppressed_p (expr, OPT_Wunused_value) |
| && !is_reference) |
| warning_at (loc, OPT_Wunused_value, "value computed is not used"); |
| expr = TREE_OPERAND (expr, 0); |
| if (TREE_CODE (expr) == CALL_EXPR |
| && (complain & tf_warning)) |
| maybe_warn_nodiscard (expr, implicit); |
| } |
| |
| break; |
| } |
| |
| case VAR_DECL: |
| { |
| /* External variables might be incomplete. */ |
| tree type = TREE_TYPE (expr); |
| int is_complete = COMPLETE_TYPE_P (complete_type (type)); |
| |
| if (TYPE_VOLATILE (type) && !is_complete && (complain & tf_warning)) |
| switch (implicit) |
| { |
| case ICV_CAST: |
| warning_at (loc, 0, "conversion to void will not access " |
| "object %qE of incomplete type %qT", expr, type); |
| break; |
| case ICV_SECOND_OF_COND: |
| warning_at (loc, 0, "variable %qE of incomplete type %qT will " |
| "not be accessed in second operand of " |
| "conditional expression", expr, type); |
| break; |
| case ICV_THIRD_OF_COND: |
| warning_at (loc, 0, "variable %qE of incomplete type %qT will " |
| "not be accessed in third operand of " |
| "conditional expression", expr, type); |
| break; |
| case ICV_RIGHT_OF_COMMA: |
| warning_at (loc, 0, "variable %qE of incomplete type %qT will " |
| "not be accessed in right operand of comma operator", |
| expr, type); |
| break; |
| case ICV_LEFT_OF_COMMA: |
| warning_at (loc, 0, "variable %qE of incomplete type %qT will " |
| "not be accessed in left operand of comma operator", |
| expr, type); |
| break; |
| case ICV_STATEMENT: |
| warning_at (loc, 0, "variable %qE of incomplete type %qT will " |
| "not be accessed in statement", expr, type); |
| break; |
| case ICV_THIRD_IN_FOR: |
| warning_at (loc, 0, "variable %qE of incomplete type %qT will " |
| "not be accessed in for increment expression", |
| expr, type); |
| break; |
| default: |
| gcc_unreachable (); |
| } |
| |
| break; |
| } |
| |
| case TARGET_EXPR: |
| /* Don't bother with the temporary object returned from a function if |
| we don't use it, don't need to destroy it, and won't abort in |
| assign_temp. We'll still |
| allocate space for it in expand_call or declare_return_variable, |
| but we don't need to track it through all the tree phases. */ |
| if (TARGET_EXPR_IMPLICIT_P (expr) |
| && !TREE_ADDRESSABLE (TREE_TYPE (expr))) |
| { |
| tree init = TARGET_EXPR_INITIAL (expr); |
| if (TREE_CODE (init) == AGGR_INIT_EXPR |
| && !AGGR_INIT_VIA_CTOR_P (init)) |
| { |
| tree fn = AGGR_INIT_EXPR_FN (init); |
| expr = build_call_array_loc (input_location, |
| TREE_TYPE (TREE_TYPE |
| (TREE_TYPE (fn))), |
| fn, |
| aggr_init_expr_nargs (init), |
| AGGR_INIT_EXPR_ARGP (init)); |
| } |
| } |
| if (complain & tf_warning) |
| maybe_warn_nodiscard (expr, implicit); |
| break; |
| |
| default:; |
| } |
| expr = resolve_nondeduced_context (expr, complain); |
| if (!mark_single_function (expr, complain)) |
| return error_mark_node; |
| |
| { |
| tree probe = expr; |
| |
| if (TREE_CODE (probe) == ADDR_EXPR) |
| probe = TREE_OPERAND (expr, 0); |
| if (type_unknown_p (probe)) |
| { |
| /* [over.over] enumerates the places where we can take the address |
| of an overloaded function, and this is not one of them. */ |
| if (complain & tf_error) |
| switch (implicit) |
| { |
| case ICV_CAST: |
| error_at (loc, "conversion to void " |
| "cannot resolve address of overloaded function"); |
| break; |
| case ICV_SECOND_OF_COND: |
| error_at (loc, "second operand of conditional expression " |
| "cannot resolve address of overloaded function"); |
| break; |
| case ICV_THIRD_OF_COND: |
| error_at (loc, "third operand of conditional expression " |
| "cannot resolve address of overloaded function"); |
| break; |
| case ICV_RIGHT_OF_COMMA: |
| error_at (loc, "right operand of comma operator " |
| "cannot resolve address of overloaded function"); |
| break; |
| case ICV_LEFT_OF_COMMA: |
| error_at (loc, "left operand of comma operator " |
| "cannot resolve address of overloaded function"); |
| break; |
| case ICV_STATEMENT: |
| error_at (loc, "statement " |
| "cannot resolve address of overloaded function"); |
| break; |
| case ICV_THIRD_IN_FOR: |
| error_at (loc, "for increment expression " |
| "cannot resolve address of overloaded function"); |
| break; |
| } |
| else |
| return error_mark_node; |
| expr = void_node; |
| } |
| else if (implicit != ICV_CAST && probe == expr && is_overloaded_fn (probe)) |
| { |
| /* Only warn when there is no &. */ |
| if (complain & tf_warning) |
| switch (implicit) |
| { |
| case ICV_SECOND_OF_COND: |
| warning_at (loc, OPT_Waddress, |
| "second operand of conditional expression " |
| "is a reference, not call, to function %qE", expr); |
| break; |
| case ICV_THIRD_OF_COND: |
| warning_at (loc, OPT_Waddress, |
| "third operand of conditional expression " |
| "is a reference, not call, to function %qE", expr); |
| break; |
| case ICV_RIGHT_OF_COMMA: |
| warning_at (loc, OPT_Waddress, |
| "right operand of comma operator " |
| "is a reference, not call, to function %qE", expr); |
| break; |
| case ICV_LEFT_OF_COMMA: |
| warning_at (loc, OPT_Waddress, |
| "left operand of comma operator " |
| "is a reference, not call, to function %qE", expr); |
| break; |
| case ICV_STATEMENT: |
| warning_at (loc, OPT_Waddress, |
| "statement is a reference, not call, to function %qE", |
| expr); |
| break; |
| case ICV_THIRD_IN_FOR: |
| warning_at (loc, OPT_Waddress, |
| "for increment expression " |
| "is a reference, not call, to function %qE", expr); |
| break; |
| default: |
| gcc_unreachable (); |
| } |
| |
| if (TREE_CODE (expr) == COMPONENT_REF) |
| expr = TREE_OPERAND (expr, 0); |
| } |
| } |
| |
| if (expr != error_mark_node && !VOID_TYPE_P (TREE_TYPE (expr))) |
| { |
| if (implicit != ICV_CAST |
| && warn_unused_value |
| && !warning_suppressed_p (expr, OPT_Wunused_value) |
| && !processing_template_decl |
| && !cp_unevaluated_operand |
| && (complain & tf_warning)) |
| { |
| /* The middle end does not warn about expressions that have |
| been explicitly cast to void, so we must do so here. */ |
| if (!TREE_SIDE_EFFECTS (expr)) |
| { |
| switch (implicit) |
| { |
| case ICV_SECOND_OF_COND: |
| warning_at (loc, OPT_Wunused_value, |
| "second operand of conditional expression " |
| "has no effect"); |
| break; |
| case ICV_THIRD_OF_COND: |
| warning_at (loc, OPT_Wunused_value, |
| "third operand of conditional expression " |
| "has no effect"); |
| break; |
| case ICV_RIGHT_OF_COMMA: |
| warning_at (loc, OPT_Wunused_value, |
| "right operand of comma operator has no effect"); |
| break; |
| case ICV_LEFT_OF_COMMA: |
| warning_at (loc, OPT_Wunused_value, |
| "left operand of comma operator has no effect"); |
| break; |
| case ICV_STATEMENT: |
| warning_at (loc, OPT_Wunused_value, |
| "statement has no effect"); |
| break; |
| case ICV_THIRD_IN_FOR: |
| warning_at (loc, OPT_Wunused_value, |
| "for increment expression has no effect"); |
| break; |
| default: |
| gcc_unreachable (); |
| } |
| } |
| else |
| { |
| tree e = expr; |
| /* We might like to warn about (say) "(int) f()", as the |
| cast has no effect, but the compiler itself will |
| generate implicit conversions under some |
| circumstances. (For example a block copy will be |
| turned into a call to "__builtin_memcpy", with a |
| conversion of the return value to an appropriate |
| type.) So, to avoid false positives, we strip |
| conversions. Do not use STRIP_NOPs because it will |
| not strip conversions to "void", as that is not a |
| mode-preserving conversion. */ |
| while (TREE_CODE (e) == NOP_EXPR) |
| e = TREE_OPERAND (e, 0); |
| |
| enum tree_code code = TREE_CODE (e); |
| enum tree_code_class tclass = TREE_CODE_CLASS (code); |
| if (tclass == tcc_comparison |
| || tclass == tcc_unary |
| || tclass == tcc_binary |
| || code == VEC_PERM_EXPR |
| || code == VEC_COND_EXPR) |
| warn_if_unused_value (e, loc); |
| } |
| } |
| expr = build1 (CONVERT_EXPR, void_type_node, expr); |
| } |
| if (! TREE_SIDE_EFFECTS (expr)) |
| expr = void_node; |
| return expr; |
| } |
| |
| /* Create an expression whose value is that of EXPR, |
| converted to type TYPE. The TREE_TYPE of the value |
| is always TYPE. This function implements all reasonable |
| conversions; callers should filter out those that are |
| not permitted by the language being compiled. |
| |
| Most of this routine is from build_reinterpret_cast. |
| |
| The back end cannot call cp_convert (what was convert) because |
| conversions to/from basetypes may involve memory references |
| (vbases) and adding or subtracting small values (multiple |
| inheritance), but it calls convert from the constant folding code |
| on subtrees of already built trees after it has ripped them apart. |
| |
| Also, if we ever support range variables, we'll probably also have to |
| do a little bit more work. */ |
| |
| tree |
| convert (tree type, tree expr) |
| { |
| tree intype; |
| |
| if (type == error_mark_node || expr == error_mark_node) |
| return error_mark_node; |
| |
| intype = TREE_TYPE (expr); |
| |
| if (INDIRECT_TYPE_P (type) && INDIRECT_TYPE_P (intype)) |
| return build_nop (type, expr); |
| |
| return ocp_convert (type, expr, CONV_BACKEND_CONVERT, |
| LOOKUP_NORMAL|LOOKUP_NO_CONVERSION, |
| tf_warning_or_error); |
| } |
| |
| /* Like convert, but in a static initializer (called from |
| convert_and_check). */ |
| |
| tree |
| convert_init (tree type, tree expr) |
| { |
| return convert (type, expr); |
| } |
| |
| /* Like cp_convert, except permit conversions to take place which |
| are not normally allowed due to access restrictions |
| (such as conversion from sub-type to private super-type). */ |
| |
| tree |
| convert_force (tree type, tree expr, int convtype, tsubst_flags_t complain) |
| { |
| tree e = expr; |
| enum tree_code code = TREE_CODE (type); |
| |
| if (code == REFERENCE_TYPE) |
| return convert_to_reference (type, e, CONV_C_CAST, 0, |
| NULL_TREE, complain); |
| |
| if (code == POINTER_TYPE) |
| return convert_to_pointer_force (type, e, complain); |
| |
| /* From typeck.cc convert_for_assignment */ |
| if (((TYPE_PTR_P (TREE_TYPE (e)) && TREE_CODE (e) == ADDR_EXPR |
| && TREE_CODE (TREE_TYPE (TREE_TYPE (e))) == METHOD_TYPE) |
| || integer_zerop (e) |
| || TYPE_PTRMEMFUNC_P (TREE_TYPE (e))) |
| && TYPE_PTRMEMFUNC_P (type)) |
| /* compatible pointer to member functions. */ |
| return build_ptrmemfunc (TYPE_PTRMEMFUNC_FN_TYPE (type), e, 1, |
| /*c_cast_p=*/1, complain); |
| |
| return ocp_convert (type, e, CONV_C_CAST|convtype, LOOKUP_NORMAL, complain); |
| } |
| |
| /* Convert an aggregate EXPR to type XTYPE. If a conversion |
| exists, return the attempted conversion. This may |
| return ERROR_MARK_NODE if the conversion is not |
| allowed (references private members, etc). |
| If no conversion exists, NULL_TREE is returned. |
| |
| FIXME: Ambiguity checking is wrong. Should choose one by the implicit |
| object parameter, or by the second standard conversion sequence if |
| that doesn't do it. This will probably wait for an overloading rewrite. |
| (jason 8/9/95) */ |
| |
| static tree |
| build_type_conversion (tree xtype, tree expr) |
| { |
| /* C++: check to see if we can convert this aggregate type |
| into the required type. */ |
| return build_user_type_conversion (xtype, expr, LOOKUP_NORMAL, |
| tf_warning_or_error); |
| } |
| |
| /* Convert the given EXPR to one of a group of types suitable for use in an |
| expression. DESIRES is a combination of various WANT_* flags (q.v.) |
| which indicates which types are suitable. If COMPLAIN is true, complain |
| about ambiguity; otherwise, the caller will deal with it. */ |
| |
| tree |
| build_expr_type_conversion (int desires, tree expr, bool complain) |
| { |
| tree basetype = TREE_TYPE (expr); |
| tree conv = NULL_TREE; |
| tree winner = NULL_TREE; |
| |
| if (null_node_p (expr) |
| && (desires & WANT_INT) |
| && !(desires & WANT_NULL)) |
| { |
| location_t loc = |
| expansion_point_location_if_in_system_header (input_location); |
| |
| warning_at (loc, OPT_Wconversion_null, |
| "converting NULL to non-pointer type"); |
| } |
| |
| if (basetype == error_mark_node) |
| return error_mark_node; |
| |
| if (! MAYBE_CLASS_TYPE_P (basetype)) |
| switch (TREE_CODE (basetype)) |
| { |
| case INTEGER_TYPE: |
| if ((desires & WANT_NULL) && null_ptr_cst_p (expr)) |
| return expr; |
| /* fall through. */ |
| |
| case BOOLEAN_TYPE: |
| return (desires & WANT_INT) ? expr : NULL_TREE; |
| case ENUMERAL_TYPE: |
| return (desires & WANT_ENUM) ? expr : NULL_TREE; |
| case REAL_TYPE: |
| return (desires & WANT_FLOAT) ? expr : NULL_TREE; |
| case POINTER_TYPE: |
| return (desires & WANT_POINTER) ? expr : NULL_TREE; |
| |
| case FUNCTION_TYPE: |
| case ARRAY_TYPE: |
| return (desires & WANT_POINTER) ? decay_conversion (expr, |
| tf_warning_or_error) |
| : NULL_TREE; |
| |
| case VECTOR_TYPE: |
| if (!gnu_vector_type_p (basetype)) |
| return NULL_TREE; |
| /* FALLTHROUGH */ |
| case COMPLEX_TYPE: |
| if ((desires & WANT_VECTOR_OR_COMPLEX) == 0) |
| return NULL_TREE; |
| switch (TREE_CODE (TREE_TYPE (basetype))) |
| { |
| case INTEGER_TYPE: |
| case BOOLEAN_TYPE: |
| return (desires & WANT_INT) ? expr : NULL_TREE; |
| case ENUMERAL_TYPE: |
| return (desires & WANT_ENUM) ? expr : NULL_TREE; |
| case REAL_TYPE: |
| return (desires & WANT_FLOAT) ? expr : NULL_TREE; |
| default: |
| return NULL_TREE; |
| } |
| |
| default: |
| return NULL_TREE; |
| } |
| |
| /* The code for conversions from class type is currently only used for |
| delete expressions. Other expressions are handled by build_new_op. */ |
| if (!complete_type_or_maybe_complain (basetype, expr, complain)) |
| return error_mark_node; |
| if (!TYPE_HAS_CONVERSION (basetype)) |
| return NULL_TREE; |
| |
| for (conv = lookup_conversions (basetype); conv; conv = TREE_CHAIN (conv)) |
| { |
| int win = 0; |
| tree candidate; |
| tree cand = TREE_VALUE (conv); |
| cand = OVL_FIRST (cand); |
| |
| if (winner && winner == cand) |
| continue; |
| |
| if (DECL_NONCONVERTING_P (cand)) |
| continue; |
| |
| candidate = non_reference (TREE_TYPE (TREE_TYPE (cand))); |
| |
| switch (TREE_CODE (candidate)) |
| { |
| case BOOLEAN_TYPE: |
| case INTEGER_TYPE: |
| win = (desires & WANT_INT); break; |
| case ENUMERAL_TYPE: |
| win = (desires & WANT_ENUM); break; |
| case REAL_TYPE: |
| win = (desires & WANT_FLOAT); break; |
| case POINTER_TYPE: |
| win = (desires & WANT_POINTER); break; |
| |
| case COMPLEX_TYPE: |
| case VECTOR_TYPE: |
| if ((desires & WANT_VECTOR_OR_COMPLEX) == 0) |
| break; |
| switch (TREE_CODE (TREE_TYPE (candidate))) |
| { |
| case BOOLEAN_TYPE: |
| case INTEGER_TYPE: |
| win = (desires & WANT_INT); break; |
| case ENUMERAL_TYPE: |
| win = (desires & WANT_ENUM); break; |
| case REAL_TYPE: |
| win = (desires & WANT_FLOAT); break; |
| default: |
| break; |
| } |
| break; |
| |
| default: |
| /* A wildcard could be instantiated to match any desired |
| type, but we can't deduce the template argument. */ |
| if (WILDCARD_TYPE_P (candidate)) |
| win = true; |
| break; |
| } |
| |
| if (win) |
| { |
| if (TREE_CODE (cand) == TEMPLATE_DECL) |
| { |
| if (complain) |
| error ("default type conversion cannot deduce template" |
| " argument for %qD", cand); |
| return error_mark_node; |
| } |
| |
| if (winner) |
| { |
| tree winner_type |
| = non_reference (TREE_TYPE (TREE_TYPE (winner))); |
| |
| if (!same_type_ignoring_top_level_qualifiers_p (winner_type, |
| candidate)) |
| { |
| if (complain) |
| { |
| error ("ambiguous default type conversion from %qT", |
| basetype); |
| inform (input_location, |
| " candidate conversions include %qD and %qD", |
| winner, cand); |
| } |
| return error_mark_node; |
| } |
| } |
| |
| winner = cand; |
| } |
| } |
| |
| if (winner) |
| { |
| tree type = non_reference (TREE_TYPE (TREE_TYPE (winner))); |
| return build_user_type_conversion (type, expr, LOOKUP_NORMAL, |
| tf_warning_or_error); |
| } |
| |
| return NULL_TREE; |
| } |
| |
| /* Implements integral promotion (4.1) and float->double promotion. */ |
| |
| tree |
| type_promotes_to (tree type) |
| { |
| tree promoted_type; |
| |
| if (type == error_mark_node) |
| return error_mark_node; |
| |
| type = TYPE_MAIN_VARIANT (type); |
| |
| /* Check for promotions of target-defined types first. */ |
| promoted_type = targetm.promoted_type (type); |
| if (promoted_type) |
| return promoted_type; |
| |
| /* bool always promotes to int (not unsigned), even if it's the same |
| size. */ |
| if (TREE_CODE (type) == BOOLEAN_TYPE) |
| type = integer_type_node; |
| |
| /* Normally convert enums to int, but convert wide enums to something |
| wider. Scoped enums don't promote, but pretend they do for backward |
| ABI bug compatibility wrt varargs. */ |
| else if (TREE_CODE (type) == ENUMERAL_TYPE |
| || type == char8_type_node |
| || type == char16_type_node |
| || type == char32_type_node |
| || type == wchar_type_node) |
| { |
| tree prom = type; |
| |
| if (TREE_CODE (type) == ENUMERAL_TYPE) |
| { |
| prom = ENUM_UNDERLYING_TYPE (prom); |
| if (!ENUM_IS_SCOPED (type) |
| && ENUM_FIXED_UNDERLYING_TYPE_P (type)) |
| { |
| /* ISO C++17, 7.6/4. A prvalue of an unscoped enumeration type |
| whose underlying type is fixed (10.2) can be converted to a |
| prvalue of its underlying type. Moreover, if integral promotion |
| can be applied to its underlying type, a prvalue of an unscoped |
| enumeration type whose underlying type is fixed can also be |
| converted to a prvalue of the promoted underlying type. */ |
| return type_promotes_to (prom); |
| } |
| } |
| |
| int precision = MAX (TYPE_PRECISION (type), |
| TYPE_PRECISION (integer_type_node)); |
| tree totype = c_common_type_for_size (precision, 0); |
| if (TYPE_UNSIGNED (prom) |
| && ! int_fits_type_p (TYPE_MAX_VALUE (prom), totype)) |
| prom = c_common_type_for_size (precision, 1); |
| else |
| prom = totype; |
| if (SCOPED_ENUM_P (type)) |
| { |
| if (abi_version_crosses (6) |
| && TYPE_MODE (prom) != TYPE_MODE (type)) |
| warning (OPT_Wabi, "scoped enum %qT passed through %<...%> as " |
| "%qT before %<-fabi-version=6%>, %qT after", |
| type, prom, ENUM_UNDERLYING_TYPE (type)); |
| if (!abi_version_at_least (6)) |
| type = prom; |
| } |
| else |
| type = prom; |
| } |
| else if (c_promoting_integer_type_p (type)) |
| { |
| /* Retain unsignedness if really not getting bigger. */ |
| if (TYPE_UNSIGNED (type) |
| && TYPE_PRECISION (type) == TYPE_PRECISION (integer_type_node)) |
| type = unsigned_type_node; |
| else |
| type = integer_type_node; |
| } |
| else if (type == float_type_node) |
| type = double_type_node; |
| |
| return type; |
| } |
| |
| /* The routines below this point are carefully written to conform to |
| the standard. They use the same terminology, and follow the rules |
| closely. Although they are used only in pt.cc at the moment, they |
| should presumably be used everywhere in the future. */ |
| |
| /* True iff EXPR can be converted to TYPE via a qualification conversion. |
| Callers should check for identical types before calling this function. */ |
| |
| bool |
| can_convert_qual (tree type, tree expr) |
| { |
| tree expr_type = TREE_TYPE (expr); |
| gcc_assert (!same_type_p (type, expr_type)); |
| |
| /* A function pointer conversion also counts as a Qualification Adjustment |
| under [over.ics.scs]. */ |
| if (fnptr_conv_p (type, expr_type)) |
| return true; |
| |
| if (TYPE_PTR_P (type) && TYPE_PTR_P (expr_type)) |
| return comp_ptr_ttypes (TREE_TYPE (type), TREE_TYPE (expr_type)); |
| else if (TYPE_PTRMEM_P (type) && TYPE_PTRMEM_P (expr_type)) |
| return (same_type_p (TYPE_PTRMEM_CLASS_TYPE (type), |
| TYPE_PTRMEM_CLASS_TYPE (expr_type)) |
| && comp_ptr_ttypes (TYPE_PTRMEM_POINTED_TO_TYPE (type), |
| TYPE_PTRMEM_POINTED_TO_TYPE (expr_type))); |
| else |
| return false; |
| } |
| |
| /* Attempt to perform qualification conversions on EXPR to convert it |
| to TYPE. Return the resulting expression, or error_mark_node if |
| the conversion was impossible. Since this is only used by |
| convert_nontype_argument, we fold the conversion. */ |
| |
| tree |
| perform_qualification_conversions (tree type, tree expr) |
| { |
| tree expr_type; |
| |
| expr_type = TREE_TYPE (expr); |
| |
| if (same_type_p (type, expr_type)) |
| return expr; |
| else if (can_convert_qual (type, expr)) |
| return cp_fold_convert (type, expr); |
| else |
| return error_mark_node; |
| } |
| |
| /* True iff T is a transaction-safe function type. */ |
| |
| bool |
| tx_safe_fn_type_p (tree t) |
| { |
| if (!FUNC_OR_METHOD_TYPE_P (t)) |
| return false; |
| return !!lookup_attribute ("transaction_safe", TYPE_ATTRIBUTES (t)); |
| } |
| |
| /* Return the transaction-unsafe variant of transaction-safe function type |
| T. */ |
| |
| tree |
| tx_unsafe_fn_variant (tree t) |
| { |
| gcc_assert (tx_safe_fn_type_p (t)); |
| tree attrs = remove_attribute ("transaction_safe", |
| TYPE_ATTRIBUTES (t)); |
| return cp_build_type_attribute_variant (t, attrs); |
| } |
| |
| /* Return true iff FROM can convert to TO by a transaction-safety |
| conversion. */ |
| |
| static bool |
| can_convert_tx_safety (tree to, tree from) |
| { |
| return (flag_tm && tx_safe_fn_type_p (from) |
| && same_type_p (to, tx_unsafe_fn_variant (from))); |
| } |
| |
| /* Return true iff FROM can convert to TO by dropping noexcept. |
| This is just a subroutine of fnptr_conv_p. */ |
| |
| static bool |
| noexcept_conv_p (tree to, tree from) |
| { |
| if (!flag_noexcept_type) |
| return false; |
| |
| if (TREE_CODE (to) != TREE_CODE (from)) |
| return false; |
| if (!FUNC_OR_METHOD_TYPE_P (from)) |
| return false; |
| if (!type_throw_all_p (to) |
| || type_throw_all_p (from)) |
| return false; |
| tree v = build_exception_variant (from, NULL_TREE); |
| return same_type_p (to, v); |
| } |
| |
| /* Return true iff FROM can convert to TO by a function pointer conversion. */ |
| |
| bool |
| fnptr_conv_p (tree to, tree from) |
| { |
| tree t = to; |
| tree f = from; |
| if (TYPE_PTRMEMFUNC_P (t) |
| && TYPE_PTRMEMFUNC_P (f)) |
| { |
| t = TYPE_PTRMEMFUNC_FN_TYPE (t); |
| f = TYPE_PTRMEMFUNC_FN_TYPE (f); |
| } |
| if (INDIRECT_TYPE_P (t) |
| && INDIRECT_TYPE_P (f)) |
| { |
| t = TREE_TYPE (t); |
| f = TREE_TYPE (f); |
| } |
| |
| return (noexcept_conv_p (t, f) |
| || can_convert_tx_safety (t, f)); |
| } |
| |
| /* Return FN with any NOP_EXPRs stripped that represent function pointer |
| conversions or conversions to the same type. */ |
| |
| tree |
| strip_fnptr_conv (tree fn) |
| { |
| while (TREE_CODE (fn) == NOP_EXPR) |
| { |
| tree op = TREE_OPERAND (fn, 0); |
| tree ft = TREE_TYPE (fn); |
| tree ot = TREE_TYPE (op); |
| if (same_type_p (ft, ot) |
| || fnptr_conv_p (ft, ot)) |
| fn = op; |
| else |
| break; |
| } |
| return fn; |
| } |