/* Language-level data type conversion for GNU C++.
- Copyright (C) 1987-2015 Free Software Foundation, Inc.
+ Copyright (C) 1987-2019 Free Software Foundation, Inc.
Hacked by Michael Tiemann (tiemann@cygnus.com)
This file is part of GCC.
#include "config.h"
#include "system.h"
#include "coretypes.h"
-#include "tm.h"
-#include "tree.h"
+#include "target.h"
+#include "cp-tree.h"
#include "stor-layout.h"
#include "flags.h"
-#include "cp-tree.h"
#include "intl.h"
#include "convert.h"
-#include "decl.h"
-#include "target.h"
-#include "wide-int.h"
+#include "stringpool.h"
+#include "attribs.h"
-static tree cp_convert_to_pointer (tree, tree, tsubst_flags_t);
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);
Here is a list of all the functions that assume that widening and
narrowing is always done with a NOP_EXPR:
- In convert.c, convert_to_integer.
+ In convert.c, convert_to_integer[_maybe_fold].
In c-typeck.c, build_binary_op_nodefault (boolean ops),
and c_common_truthvalue_conversion.
In expr.c: expand_expr, for operands of a MULT_EXPR.
else try C-style pointer conversion. */
static tree
-cp_convert_to_pointer (tree type, tree expr, tsubst_flags_t complain)
+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 = EXPR_LOC_OR_LOC (expr, input_location);
+ location_t loc = cp_expr_loc_or_loc (expr, input_location);
if (intype == error_mark_node)
return error_mark_node;
if (!COMPLETE_TYPE_P (intype))
{
if (complain & tf_error)
- error_at (loc, "can%'t convert from incomplete type %qT to %qT",
+ error_at (loc, "can%'t convert from incomplete type %qH to %qI",
intype, type);
return error_mark_node;
}
{
if ((complain & tf_error)
&& rval == error_mark_node)
- error_at (loc, "conversion of %qE from %qT to %qT is ambiguous",
+ error_at (loc, "conversion of %qE from %qH to %qI is ambiguous",
expr, intype, type);
return rval;
}
form = TREE_CODE (intype);
- if (POINTER_TYPE_P (intype))
+ if (INDIRECT_TYPE_P (intype))
{
intype = TYPE_MAIN_VARIANT (intype);
if (TYPE_PTRMEMFUNC_P (type))
{
if (complain & tf_error)
- error_at (loc, "cannot convert %qE from type %qT to type %qT",
+ error_at (loc, "cannot convert %qE from type %qH to type %qI",
expr, intype, type);
return error_mark_node;
}
{
if (TREE_CODE (expr) == PTRMEM_CST)
return cp_convert_to_pointer (type, PTRMEM_CST_MEMBER (expr),
- complain);
+ dofold, complain);
else if (TREE_CODE (expr) == OFFSET_REF)
{
tree object = TREE_OPERAND (expr, 0);
}
}
if (complain & tf_error)
- error_at (loc, "cannot convert %qE from type %qT to type %qT",
+ error_at (loc, "cannot convert %qE from type %qH to type %qI",
expr, intype, type);
return error_mark_node;
}
else if (TYPE_PTRMEM_P (type) && INTEGRAL_CODE_P (form))
{
if (complain & tf_error)
- error_at (loc, "invalid conversion from %qT to %qT", intype, type);
+ error_at (loc, "invalid conversion from %qH to %qI", intype, type);
return error_mark_node;
}
/* 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 (TYPE_MODE (TREE_TYPE (expr)))
- == GET_MODE_SIZE (TYPE_MODE (type)));
+ 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 (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 %qT to type %qT",
+ error_at (loc, "cannot convert %qE from type %qH to type %qI",
expr, intype, type);
return error_mark_node;
}
}
}
- return cp_convert_to_pointer (type, expr, complain);
+ return cp_convert_to_pointer (type, expr, /*fold*/false, complain);
}
/* We are passing something to a function which requires a reference.
tree argtype = TREE_TYPE (arg);
tree target_type = TREE_TYPE (type);
- gcc_assert (TREE_CODE (type) == REFERENCE_TYPE);
+ gcc_assert (TYPE_REF_P (type));
- if ((flags & DIRECT_BIND) && ! real_lvalue_p (arg))
+ 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. */
cp_finish_decl (arg, targ, /*init_const_expr_p=*/false, NULL_TREE,
LOOKUP_ONLYCONVERTING|DIRECT_BIND);
}
- else if (!(flags & DIRECT_BIND) && ! lvalue_p (arg))
+ 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
{
tree ttl = TREE_TYPE (reftype);
- if (!CP_TYPE_CONST_NON_VOLATILE_P (ttl))
+ if (!TYPE_REF_IS_RVALUE (reftype)
+ && !CP_TYPE_CONST_NON_VOLATILE_P (ttl))
{
const char *msg;
tree rval = NULL_TREE;
tree rval_as_conversion = NULL_TREE;
bool can_convert_intype_to_type;
- location_t loc = EXPR_LOC_OR_LOC (expr, input_location);
+ location_t loc = cp_expr_loc_or_loc (expr, input_location);
if (TREE_CODE (type) == FUNCTION_TYPE
&& TREE_TYPE (expr) == unknown_type_node)
intype = TREE_TYPE (expr);
- gcc_assert (TREE_CODE (intype) != REFERENCE_TYPE);
- gcc_assert (TREE_CODE (reftype) == REFERENCE_TYPE);
+ gcc_assert (!TYPE_REF_P (intype));
+ gcc_assert (TYPE_REF_P (reftype));
intype = TYPE_MAIN_VARIANT (intype);
= build_type_conversion (reftype, expr);
if (rval_as_conversion && rval_as_conversion != error_mark_node
- && real_lvalue_p (rval_as_conversion))
+ && lvalue_p (rval_as_conversion))
{
expr = rval_as_conversion;
rval_as_conversion = NULL_TREE;
tree ttr = lvalue_type (expr);
if ((complain & tf_error)
- && ! real_lvalue_p (expr))
+ && ! 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 %qT to %qT discards qualifiers",
+ 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) && lvalue_p (expr))
+ 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
}
if (complain & tf_error)
- error_at (loc, "cannot convert type %qT to type %qT", intype, reftype);
+ error_at (loc, "cannot convert type %qH to type %qI", intype, reftype);
return error_mark_node;
}
convert_from_reference (tree val)
{
if (TREE_TYPE (val)
- && TREE_CODE (TREE_TYPE (val)) == REFERENCE_TYPE)
+ && TYPE_REF_P (TREE_TYPE (val)))
{
tree t = TREE_TYPE (TREE_TYPE (val));
tree ref = build1 (INDIRECT_REF, t, val);
static tree
ignore_overflows (tree expr, tree orig)
{
- if (TREE_CODE (expr) == INTEGER_CST
- && TREE_CODE (orig) == INTEGER_CST
- && TREE_OVERFLOW (expr) != TREE_OVERFLOW (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 (orig));
+ gcc_assert (!TREE_OVERFLOW (stripped_orig));
/* Ensure constant sharing. */
- expr = wide_int_to_tree (TREE_TYPE (expr), expr);
+ stripped_expr = wide_int_to_tree (TREE_TYPE (stripped_expr),
+ wi::to_wide (stripped_expr));
}
- return expr;
+
+ return preserve_any_location_wrapper (stripped_expr, expr);
}
/* Fold away simple conversions, but make sure TREE_OVERFLOW is set
tree
cp_fold_convert (tree type, tree expr)
{
- tree conv = fold_convert (type, expr);
- conv = ignore_overflows (conv, 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);
+ }
return conv;
}
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 = maybe_constant_value (expr);
- tree stripped = folded;
- tree folded_result
- = folded != expr ? cp_convert (type, folded, complain) : result;
-
- /* maybe_constant_value wraps an INTEGER_CST with TREE_OVERFLOW in a
- NOP_EXPR so that it isn't TREE_CONSTANT anymore. */
- STRIP_NOPS (stripped);
-
- if (!TREE_OVERFLOW_P (stripped)
+ 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 (input_location, type, folded,
- folded_result);
+ warnings_for_convert_and_check (cp_expr_loc_or_loc (expr, input_location),
+ type, folded, folded_result);
}
return result;
enum tree_code code = TREE_CODE (type);
const char *invalid_conv_diag;
tree e1;
- location_t loc = EXPR_LOC_OR_LOC (expr, input_location);
+ location_t loc = cp_expr_loc_or_loc (expr, input_location);
+ bool dofold = (convtype & CONV_FOLD);
if (error_operand_p (e) || type == error_mark_node)
return error_mark_node;
}
/* FIXME remove when moving to c_fully_fold model. */
- e = scalar_constant_value (e);
+ if (!CLASS_TYPE_P (type))
+ {
+ e = mark_rvalue_use (e);
+ e = scalar_constant_value (e);
+ }
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)))
{
- if (same_type_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
/* For complex data types, we need to perform componentwise
conversion. */
else if (TREE_CODE (type) == COMPLEX_TYPE)
- return fold_if_not_in_template (convert_to_complex (type, e));
- else if (TREE_CODE (type) == VECTOR_TYPE)
- return fold_if_not_in_template (convert_to_vector (type, e));
+ return convert_to_complex_maybe_fold (type, e, dofold);
+ else if (VECTOR_TYPE_P (type))
+ return convert_to_vector (type, 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 fold_if_not_in_template (build_nop (type, e));
+ return build_nop (type, e);
}
}
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 (e) == INTEGER_CST
+ && TREE_CODE (val) == INTEGER_CST
&& ENUM_UNDERLYING_TYPE (type)
- && !int_fits_type_p (e, 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",
to the underlying type first. */
if (SCOPED_ENUM_P (intype) && (convtype & CONV_STATIC))
e = build_nop (ENUM_UNDERLYING_TYPE (intype), e);
- return cp_truthvalue_conversion (e);
+ if (complain & tf_warning)
+ return cp_truthvalue_conversion (e);
+ 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);
+ return cp_truthvalue_conversion (e);
+ }
}
- converted = fold_if_not_in_template (convert_to_integer (type, e));
+ converted = convert_to_integer_maybe_fold (type, e, dofold);
/* Ignore any integer overflow caused by the conversion. */
return ignore_overflows (converted, e);
}
- if (NULLPTR_TYPE_P (type) && e && null_ptr_cst_p (e))
- {
- if (complain & tf_warning)
- maybe_warn_zero_as_null_pointer_constant (e, loc);
- return nullptr_node;
- }
- if (POINTER_TYPE_P (type) || TYPE_PTRMEM_P (type))
- return fold_if_not_in_template (cp_convert_to_pointer (type, e, complain));
+ 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);
in_vtype, type);
return error_mark_node;
}
- return fold_if_not_in_template (convert_to_vector (type, e));
+ return convert_to_vector (type, e);
}
if (code == REAL_TYPE || code == COMPLEX_TYPE)
{
TREE_TYPE (e));
}
if (code == REAL_TYPE)
- return fold_if_not_in_template (convert_to_real (type, e));
+ return convert_to_real_maybe_fold (type, e, dofold);
else if (code == COMPLEX_TYPE)
- return fold_if_not_in_template (convert_to_complex (type, e));
+ return convert_to_complex_maybe_fold (type, e, dofold);
}
/* New C++ semantics: since assignment is now based on
{
/* 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 (expr, complain))
+ if (invalid_nonstatic_memfn_p (loc, expr, complain))
/* We displayed the error message. */;
else
- error_at (loc, "conversion from %qT to non-scalar type %qT requested",
+ 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 == unknown_type_node)
+ return NULL_TREE;
+ gcc_assert (INDIRECT_TYPE_P (type));
+ if (fold)
+ fn = maybe_constant_init (fn);
+ STRIP_NOPS (fn);
+ if (TREE_CODE (fn) == ADDR_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)
+{
+ tree call = expr;
+ if (TREE_CODE (expr) == TARGET_EXPR)
+ call = TARGET_EXPR_INITIAL (expr);
+ location_t loc = cp_expr_loc_or_loc (call, input_location);
+ 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);
+ if (implicit != ICV_CAST && fn
+ && lookup_attribute ("nodiscard", DECL_ATTRIBUTES (fn)))
+ {
+ auto_diagnostic_group d;
+ if (warning_at (loc, OPT_Wunused_result,
+ "ignoring return value of %qD, "
+ "declared with attribute nodiscard", fn))
+ inform (DECL_SOURCE_LOCATION (fn), "declared here");
+ }
+ else if (implicit != ICV_CAST
+ && lookup_attribute ("nodiscard", TYPE_ATTRIBUTES (rettype)))
+ {
+ auto_diagnostic_group d;
+ if (warning_at (loc, OPT_Wunused_result,
+ "ignoring returned value of type %qT, "
+ "declared with attribute nodiscard", rettype))
+ {
+ 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
tree
convert_to_void (tree expr, impl_conv_void implicit, tsubst_flags_t complain)
{
- location_t loc = EXPR_LOC_OR_LOC (expr, input_location);
+ location_t loc = cp_expr_loc_or_loc (expr, input_location);
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);
- else
- {
- tree exprv = expr;
-
- while (TREE_CODE (exprv) == COMPOUND_EXPR)
- exprv = TREE_OPERAND (exprv, 1);
- if (DECL_P (exprv)
- || handled_component_p (exprv)
- || INDIRECT_REF_P (exprv))
- /* Expr is not being 'used' here, otherwise we whould have
- called mark_{rl}value_use use here, which would have in turn
- called mark_exp_read. Rather, we call mark_exp_read directly
- to avoid some warnings when
- -Wunused-but-set-{variable,parameter} is in effect. */
- mark_exp_read (exprv);
- }
if (!TREE_TYPE (expr))
return expr;
- if (invalid_nonstatic_memfn_p (expr, complain))
+ if (invalid_nonstatic_memfn_p (loc, expr, complain))
return error_mark_node;
if (TREE_CODE (expr) == PSEUDO_DTOR_EXPR)
{
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 cdtors
+ are nonsensical), 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;
+
+ maybe_warn_nodiscard (expr, implicit);
break;
case INDIRECT_REF:
{
tree type = TREE_TYPE (expr);
- int is_reference = TREE_CODE (TREE_TYPE (TREE_OPERAND (expr, 0)))
- == REFERENCE_TYPE;
+ 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));
&& !is_reference)
warning_at (loc, OPT_Wunused_value, "value computed is not used");
expr = TREE_OPERAND (expr, 0);
+ if (TREE_CODE (expr) == CALL_EXPR)
+ maybe_warn_nodiscard (expr, implicit);
}
break;
case TARGET_EXPR:
/* Don't bother with the temporary object returned from a function if
- we don't use it and don't need to destroy it. We'll still
+ 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)
- && TYPE_HAS_TRIVIAL_DESTRUCTOR (TREE_TYPE (expr)))
+ && !TREE_ADDRESSABLE (TREE_TYPE (expr)))
{
tree init = TARGET_EXPR_INITIAL (expr);
if (TREE_CODE (init) == AGGR_INIT_EXPR
{
tree fn = AGGR_INIT_EXPR_FN (init);
expr = build_call_array_loc (input_location,
- TREE_TYPE (TREE_TYPE (TREE_TYPE (fn))),
+ TREE_TYPE (TREE_TYPE
+ (TREE_TYPE (fn))),
fn,
aggr_init_expr_nargs (init),
AGGR_INIT_EXPR_ARGP (init));
}
}
+ maybe_warn_nodiscard (expr, implicit);
break;
default:;
}
- expr = resolve_nondeduced_context (expr);
+ expr = resolve_nondeduced_context (expr, complain);
{
tree probe = expr;
intype = TREE_TYPE (expr);
- if (POINTER_TYPE_P (type) && POINTER_TYPE_P (intype))
- return fold_if_not_in_template (build_nop (type, expr));
+ if (INDIRECT_TYPE_P (type) && INDIRECT_TYPE_P (intype))
+ return build_nop (type, expr);
- return ocp_convert (type, expr, CONV_OLD_CONVERT,
+ return ocp_convert (type, expr, CONV_BACKEND_CONVERT,
LOOKUP_NORMAL|LOOKUP_NO_CONVERSION,
tf_warning_or_error);
}
enum tree_code code = TREE_CODE (type);
if (code == REFERENCE_TYPE)
- return (fold_if_not_in_template
- (convert_to_reference (type, e, CONV_C_CAST, 0,
- NULL_TREE, complain)));
+ return convert_to_reference (type, e, CONV_C_CAST, 0,
+ NULL_TREE, complain);
if (code == POINTER_TYPE)
- return fold_if_not_in_template (convert_to_pointer_force (type, e,
- complain));
+ return convert_to_pointer_force (type, e, complain);
/* From typeck.c convert_for_assignment */
if (((TYPE_PTR_P (TREE_TYPE (e)) && TREE_CODE (e) == ADDR_EXPR
tree conv = NULL_TREE;
tree winner = NULL_TREE;
- if (expr == null_node
+ if (null_node_p (expr)
&& (desires & WANT_INT)
&& !(desires & WANT_NULL))
{
- source_location loc =
+ location_t loc =
expansion_point_location_if_in_system_header (input_location);
warning_at (loc, OPT_Wconversion_null,
case INTEGER_TYPE:
if ((desires & WANT_NULL) && null_ptr_cst_p (expr))
return expr;
- /* else fall through... */
+ /* fall through. */
case BOOLEAN_TYPE:
return (desires & WANT_INT) ? expr : NULL_TREE;
int win = 0;
tree candidate;
tree cand = TREE_VALUE (conv);
- cand = OVL_CURRENT (cand);
+ cand = OVL_FIRST (cand);
if (winner && winner == cand)
continue;
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);
- tree prom = type;
- if (TREE_CODE (prom) == ENUMERAL_TYPE)
- prom = ENUM_UNDERLYING_TYPE (prom);
if (TYPE_UNSIGNED (prom)
&& ! int_fits_type_p (TYPE_MAX_VALUE (prom), totype))
prom = c_common_type_for_size (precision, 1);
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",
+ "%qT before %<-fabi-version=6%>, %qT after",
type, prom, ENUM_UNDERLYING_TYPE (type));
if (!abi_version_at_least (6))
type = prom;
closely. Although they are used only in pt.c 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));
+
+ 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. */
+ 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)
if (same_type_p (type, expr_type))
return expr;
- else if (TYPE_PTR_P (type) && TYPE_PTR_P (expr_type)
- && comp_ptr_ttypes (TREE_TYPE (type), TREE_TYPE (expr_type)))
- return build_nop (type, expr);
- else if (TYPE_PTRMEM_P (type) && TYPE_PTRMEM_P (expr_type)
- && 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)))
- return build_nop (type, 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 (TREE_CODE (t) != FUNCTION_TYPE
+ && TREE_CODE (t) != METHOD_TYPE)
+ 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. */
+
+static bool
+noexcept_conv_p (tree to, tree from)
+{
+ if (!flag_noexcept_type)
+ return false;
+
+ tree t = non_reference (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 (TYPE_PTR_P (t)
+ && TYPE_PTR_P (f))
+ {
+ t = TREE_TYPE (t);
+ f = TREE_TYPE (f);
+ }
+ tree_code code = TREE_CODE (f);
+ if (TREE_CODE (t) != code)
+ return false;
+ if (code != FUNCTION_TYPE && code != METHOD_TYPE)
+ return false;
+ if (!type_throw_all_p (t)
+ || type_throw_all_p (f))
+ return false;
+ tree v = build_exception_variant (f, NULL_TREE);
+ return same_type_p (t, v);
+}
+
+/* Return true iff FROM can convert to TO by a function pointer conversion. */
+
+bool
+fnptr_conv_p (tree to, tree from)
+{
+ tree t = non_reference (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 (TYPE_PTR_P (t)
+ && TYPE_PTR_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;
+}