The problem here was that the call to convert_to_integer_nofold was
still pushing the conversion down into the multiply expression, and when
we do the multiplication in unsigned short it overflows. This patch
fixes convert_to_integer_1 to not do any truncation distribution when
!dofold. But that broke several testcases, because fold() doesn't ever
try to do that distribution, and so was missing some optimizations
revealed by the distribution. So this patch changes cp_fold to redo the
conversion with dofold enabled. I also change the C++ convert() entry
point to do folding conversion again, since a few places in
convert_to_integer_1, and probably elsewhere in the back end, expect that.
For this email I didn't re-indent the truncation distribution code in
convert_to_integer_1 to make it easier to read; for what I check in
should I leave it like this, re-indent it, or do something else like a
goto to avoid the need for re-indentation?
Tested x86_64-pc-linux-gnu, OK for trunk?
commit 11d9ebd11a5951c60e132456075cd4dc42ff7e71
Author: Jason Merrill <ja...@redhat.com>
Date: Wed Feb 3 16:57:42 2016 -0500
PR c++/69631
* convert.c (convert_to_integer_1): Check dofold on truncation
distribution.
diff --git a/gcc/convert.c b/gcc/convert.c
index dd7d818..b828bdf 100644
--- a/gcc/convert.c
+++ b/gcc/convert.c
@@ -105,12 +105,12 @@ convert_to_pointer (tree type, tree expr)
}
/* A wrapper around convert_to_pointer_1 that only folds the
- expression if it is CONSTANT_CLASS_P. */
+ expression if DOFOLD, or if it is CONSTANT_CLASS_P. */
tree
-convert_to_pointer_nofold (tree type, tree expr)
+convert_to_pointer_maybe_fold (tree type, tree expr, bool dofold)
{
- return convert_to_pointer_1 (type, expr, CONSTANT_CLASS_P (expr));
+ return convert_to_pointer_1 (type, expr, dofold || CONSTANT_CLASS_P (expr));
}
/* Convert EXPR to some floating-point type TYPE.
@@ -403,12 +403,12 @@ convert_to_real (tree type, tree expr)
}
/* A wrapper around convert_to_real_1 that only folds the
- expression if it is CONSTANT_CLASS_P. */
+ expression if DOFOLD, or if it is CONSTANT_CLASS_P. */
tree
-convert_to_real_nofold (tree type, tree expr)
+convert_to_real_maybe_fold (tree type, tree expr, bool dofold)
{
- return convert_to_real_1 (type, expr, CONSTANT_CLASS_P (expr));
+ return convert_to_real_1 (type, expr, dofold || CONSTANT_CLASS_P (expr));
}
/* Convert EXPR to some integer (or enum) type TYPE.
@@ -669,6 +669,7 @@ convert_to_integer_1 (tree type, tree expr, bool dofold)
two narrow values can be combined in their narrow type even to
make a wider result--are handled by "shorten" in build_binary_op. */
+ if (dofold)
switch (ex_form)
{
case RSHIFT_EXPR:
@@ -857,9 +858,6 @@ convert_to_integer_1 (tree type, tree expr, bool dofold)
/* This is not correct for ABS_EXPR,
since we must test the sign before truncation. */
{
- if (!dofold)
- break;
-
/* Do the arithmetic in type TYPEX,
then convert result to TYPE. */
tree typex = type;
@@ -895,7 +893,6 @@ convert_to_integer_1 (tree type, tree expr, bool dofold)
the conditional and never loses. A COND_EXPR may have a throw
as one operand, which then has void type. Just leave void
operands as they are. */
- if (dofold)
return
fold_build3 (COND_EXPR, type, TREE_OPERAND (expr, 0),
VOID_TYPE_P (TREE_TYPE (TREE_OPERAND (expr, 1)))
@@ -968,19 +965,13 @@ convert_to_integer (tree type, tree expr)
return convert_to_integer_1 (type, expr, true);
}
-/* Convert EXPR to some integer (or enum) type TYPE.
-
- EXPR must be pointer, integer, discrete (enum, char, or bool), float,
- fixed-point or vector; in other cases error is called.
-
- The result of this is always supposed to be a newly created tree node
- not in use in any existing structure. The tree node isn't folded,
- beside EXPR is of constant class. */
+/* A wrapper around convert_to_complex_1 that only folds the
+ expression if DOFOLD, or if it is CONSTANT_CLASS_P. */
tree
-convert_to_integer_nofold (tree type, tree expr)
+convert_to_integer_maybe_fold (tree type, tree expr, bool fold)
{
- return convert_to_integer_1 (type, expr, CONSTANT_CLASS_P (expr));
+ return convert_to_integer_1 (type, expr, fold || CONSTANT_CLASS_P (expr));
}
/* Convert EXPR to the complex type TYPE in the usual ways. If FOLD_P is
@@ -1059,12 +1050,12 @@ convert_to_complex (tree type, tree expr)
}
/* A wrapper around convert_to_complex_1 that only folds the
- expression if it is CONSTANT_CLASS_P. */
+ expression if DOFOLD, or if it is CONSTANT_CLASS_P. */
tree
-convert_to_complex_nofold (tree type, tree expr)
+convert_to_complex_maybe_fold (tree type, tree expr, bool dofold)
{
- return convert_to_complex_1 (type, expr, CONSTANT_CLASS_P (expr));
+ return convert_to_complex_1 (type, expr, dofold || CONSTANT_CLASS_P (expr));
}
/* Convert EXPR to the vector type TYPE in the usual ways. */
diff --git a/gcc/convert.h b/gcc/convert.h
index aa8fb9b..bee280d 100644
--- a/gcc/convert.h
+++ b/gcc/convert.h
@@ -21,14 +21,23 @@ along with GCC; see the file COPYING3. If not see
#define GCC_CONVERT_H
extern tree convert_to_integer (tree, tree);
-extern tree convert_to_integer_nofold (tree, tree);
+extern tree convert_to_integer_maybe_fold (tree, tree, bool);
extern tree convert_to_pointer (tree, tree);
-extern tree convert_to_pointer_nofold (tree, tree);
+extern tree convert_to_pointer_maybe_fold (tree, tree, bool);
extern tree convert_to_real (tree, tree);
-extern tree convert_to_real_nofold (tree, tree);
+extern tree convert_to_real_maybe_fold (tree, tree, bool);
extern tree convert_to_fixed (tree, tree);
extern tree convert_to_complex (tree, tree);
-extern tree convert_to_complex_nofold (tree, tree);
+extern tree convert_to_complex_maybe_fold (tree, tree, bool);
extern tree convert_to_vector (tree, tree);
+extern inline tree convert_to_integer_nofold (tree t, tree x)
+{ return convert_to_integer_maybe_fold (t, x, false); }
+extern inline tree convert_to_pointer_nofold (tree t, tree x)
+{ return convert_to_pointer_maybe_fold (t, x, false); }
+extern inline tree convert_to_real_nofold (tree t, tree x)
+{ return convert_to_real_maybe_fold (t, x, false); }
+extern inline tree convert_to_complex_nofold (tree t, tree x)
+{ return convert_to_complex_maybe_fold (t, x, false); }
+
#endif /* GCC_CONVERT_H */
diff --git a/gcc/cp/cp-gimplify.c b/gcc/cp/cp-gimplify.c
index bb81534..d83e9de 100644
--- a/gcc/cp/cp-gimplify.c
+++ b/gcc/cp/cp-gimplify.c
@@ -1953,7 +1953,13 @@ cp_fold (tree x)
loc = EXPR_LOCATION (x);
op0 = cp_fold_maybe_rvalue (TREE_OPERAND (x, 0), rval_ops);
- if (op0 != TREE_OPERAND (x, 0))
+ if (code == CONVERT_EXPR
+ && SCALAR_TYPE_P (TREE_TYPE (x))
+ && op0 != void_node)
+ /* During parsing we used convert_to_*_nofold; re-convert now using the
+ folding variants, since fold() doesn't do those transformations. */
+ x = fold (convert (TREE_TYPE (x), op0));
+ else if (op0 != TREE_OPERAND (x, 0))
{
if (op0 == error_mark_node)
x = error_mark_node;
diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h
index 0aeee57..786927b 100644
--- a/gcc/cp/cp-tree.h
+++ b/gcc/cp/cp-tree.h
@@ -5019,10 +5019,12 @@ enum overload_flags { NO_SPECIAL = 0, DTOR_FLAG, TYPENAME_FLAG };
#define CONV_PRIVATE 16
/* #define CONV_NONCONVERTING 32 */
#define CONV_FORCE_TEMP 64
+#define CONV_FOLD 128
#define CONV_OLD_CONVERT (CONV_IMPLICIT | CONV_STATIC | CONV_CONST \
| CONV_REINTERPRET)
#define CONV_C_CAST (CONV_IMPLICIT | CONV_STATIC | CONV_CONST \
| CONV_REINTERPRET | CONV_PRIVATE | CONV_FORCE_TEMP)
+#define CONV_BACKEND_CONVERT (CONV_OLD_CONVERT | CONV_FOLD)
/* Used by build_expr_type_conversion to indicate which types are
acceptable as arguments to the expression under consideration. */
diff --git a/gcc/cp/cvt.c b/gcc/cp/cvt.c
index f381f9b..60362fd 100644
--- a/gcc/cp/cvt.c
+++ b/gcc/cp/cvt.c
@@ -34,7 +34,6 @@ along with GCC; see the file COPYING3. If not see
#include "intl.h"
#include "convert.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);
@@ -50,7 +49,7 @@ static void diagnose_ref_binding (location_t, tree, tree, tree);
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[_nofold].
+ 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.
@@ -70,7 +69,8 @@ static void diagnose_ref_binding (location_t, tree, tree, tree);
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;
@@ -185,7 +185,7 @@ cp_convert_to_pointer (tree type, tree expr, tsubst_flags_t complain)
{
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);
@@ -237,7 +237,7 @@ cp_convert_to_pointer (tree type, tree expr, tsubst_flags_t complain)
gcc_assert (GET_MODE_SIZE (TYPE_MODE (TREE_TYPE (expr)))
== GET_MODE_SIZE (TYPE_MODE (type)));
- return convert_to_pointer_nofold (type, expr);
+ return convert_to_pointer_maybe_fold (type, expr, dofold);
}
if (type_unknown_p (expr))
@@ -296,7 +296,7 @@ convert_to_pointer_force (tree type, tree expr, tsubst_flags_t complain)
}
}
- 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.
@@ -670,6 +670,7 @@ ocp_convert (tree type, tree expr, int convtype, int flags,
const char *invalid_conv_diag;
tree e1;
location_t loc = 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;
@@ -706,7 +707,7 @@ ocp_convert (tree type, tree expr, int convtype, int flags,
/* For complex data types, we need to perform componentwise
conversion. */
else if (TREE_CODE (type) == COMPLEX_TYPE)
- return convert_to_complex_nofold (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)
@@ -799,7 +800,7 @@ ocp_convert (tree type, tree expr, int convtype, int flags,
return cp_truthvalue_conversion (e);
}
- converted = convert_to_integer_nofold (type, e);
+ converted = convert_to_integer_maybe_fold (type, e, dofold);
/* Ignore any integer overflow caused by the conversion. */
return ignore_overflows (converted, e);
@@ -811,7 +812,7 @@ ocp_convert (tree type, tree expr, int convtype, int flags,
return nullptr_node;
}
if (POINTER_TYPE_P (type) || TYPE_PTRMEM_P (type))
- return cp_convert_to_pointer (type, e, complain);
+ return cp_convert_to_pointer (type, e, dofold, complain);
if (code == VECTOR_TYPE)
{
tree in_vtype = TREE_TYPE (e);
@@ -842,9 +843,9 @@ ocp_convert (tree type, tree expr, int convtype, int flags,
TREE_TYPE (e));
}
if (code == REAL_TYPE)
- return convert_to_real_nofold (type, e);
+ return convert_to_real_maybe_fold (type, e, dofold);
else if (code == COMPLEX_TYPE)
- return convert_to_complex_nofold (type, e);
+ return convert_to_complex_maybe_fold (type, e, dofold);
}
/* New C++ semantics: since assignment is now based on
@@ -1460,7 +1461,7 @@ convert (tree type, tree expr)
if (POINTER_TYPE_P (type) && POINTER_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);
}
diff --git a/gcc/testsuite/g++.dg/delayedfold/fwrapv1.C b/gcc/testsuite/g++.dg/delayedfold/fwrapv1.C
new file mode 100644
index 0000000..412535c
--- /dev/null
+++ b/gcc/testsuite/g++.dg/delayedfold/fwrapv1.C
@@ -0,0 +1,6 @@
+// PR c++/69631
+// { dg-options -fwrapv }
+
+struct C {
+ static const unsigned short max = static_cast<unsigned short>((32767 * 2 + 1));
+};