* [PATCH] c++: Deprecate arithmetic convs on different enums [PR97573]
@ 2020-10-28 18:01 Marek Polacek
2020-10-28 18:43 ` Jason Merrill
0 siblings, 1 reply; 4+ messages in thread
From: Marek Polacek @ 2020-10-28 18:01 UTC (permalink / raw)
To: Jason Merrill, GCC Patches
I noticed that C++20 P1120R0 deprecated certain arithmetic conversions
as outlined in [depr.arith.conv.enum], but we don't warn about them. In
particular, "If one operand is of enumeration type and the other operand
is of a different enumeration type or a floating-point type, this
behavior is deprecated." These will likely become ill-formed in C++23,
so we should warn by default in C++20. To this effect, this patch adds
two new warnings (like clang++): -Wdeprecated-enum-enum-conversion and
-Wdeprecated-enum-float-conversion. They are enabled by default in
C++20. In older dialects, to enable these warnings you can now use
-Wenum-conversion which I made available in C++ too. Note that unlike
C, in C++ it is not enabled by -Wextra, because that breaks bootstrap.
We already warn about comparisons of two different enumeration types via
-Wenum-compare, the rest is handled in this patch: we're performing the
usual arithmetic conversions in these contexts:
- an arithmetic operation,
- a bitwise operation,
- a comparison,
- a conditional operator,
- a compound assign operator.
Using the spaceship operator as enum <=> real_type is ill-formed but we
don't reject it yet. We should also address [depr.array.comp] too, but
it's not handled in this patch.
Bootstrapped/regtested on x86_64-pc-linux-gnu, ok for trunk?
gcc/c-family/ChangeLog:
PR c++/97573
* c-opts.c (c_common_post_options): In C++20, turn on
-Wdeprecated-enum-enum-conversion and
-Wdeprecated-enum-float-conversion.
* c.opt (Wdeprecated-enum-enum-conversion,
Wdeprecated-enum-float-conversion): New options.
(Wenum-conversion): Allow for C++ too.
gcc/cp/ChangeLog:
PR c++/97573
* call.c (build_conditional_expr_1): Warn about the deprecated
enum/real type conversion in C++20. Also warn about a non-enumerated
and enumerated type in ?: when -Wenum-conversion is on.
* typeck.c (do_warn_enum_conversions): New function.
(cp_build_binary_op): Call it.
gcc/ChangeLog:
PR c++/97573
* doc/invoke.texi: Document -Wdeprecated-enum-enum-conversion
and -Wdeprecated-enum-float-conversion. -Wenum-conversion is
no longer C/ObjC only.
gcc/testsuite/ChangeLog:
PR c++/97573
* g++.dg/cpp0x/linkage2.C: Add dg-warning.
* g++.dg/parse/attr3.C: Likewise.
* g++.dg/cpp2a/enum-conv1.C: New test.
* g++.dg/cpp2a/enum-conv2.C: New test.
* g++.dg/cpp2a/enum-conv3.C: New test.
---
gcc/c-family/c-opts.c | 10 ++
gcc/c-family/c.opt | 11 ++-
gcc/cp/call.c | 35 +++++--
gcc/cp/typeck.c | 112 +++++++++++++++++++++-
gcc/doc/invoke.texi | 44 ++++++++-
gcc/testsuite/g++.dg/cpp0x/linkage2.C | 2 +-
gcc/testsuite/g++.dg/cpp2a/enum-conv1.C | 120 ++++++++++++++++++++++++
gcc/testsuite/g++.dg/cpp2a/enum-conv2.C | 115 +++++++++++++++++++++++
gcc/testsuite/g++.dg/cpp2a/enum-conv3.C | 115 +++++++++++++++++++++++
gcc/testsuite/g++.dg/parse/attr3.C | 2 +-
10 files changed, 549 insertions(+), 17 deletions(-)
create mode 100644 gcc/testsuite/g++.dg/cpp2a/enum-conv1.C
create mode 100644 gcc/testsuite/g++.dg/cpp2a/enum-conv2.C
create mode 100644 gcc/testsuite/g++.dg/cpp2a/enum-conv3.C
diff --git a/gcc/c-family/c-opts.c b/gcc/c-family/c-opts.c
index 38d33849423..120f4489f6c 100644
--- a/gcc/c-family/c-opts.c
+++ b/gcc/c-family/c-opts.c
@@ -925,6 +925,16 @@ c_common_post_options (const char **pfilename)
SET_OPTION_IF_UNSET (&global_options, &global_options_set, warn_volatile,
cxx_dialect >= cxx20 && warn_deprecated);
+ /* -Wdeprecated-enum-enum-conversion is enabled by default in C++20. */
+ SET_OPTION_IF_UNSET (&global_options, &global_options_set,
+ warn_deprecated_enum_enum_conv,
+ cxx_dialect >= cxx20 && warn_deprecated);
+
+ /* -Wdeprecated-enum-float-conversion is enabled by default in C++20. */
+ SET_OPTION_IF_UNSET (&global_options, &global_options_set,
+ warn_deprecated_enum_float_conv,
+ cxx_dialect >= cxx20 && warn_deprecated);
+
/* Declone C++ 'structors if -Os. */
if (flag_declone_ctor_dtor == -1)
flag_declone_ctor_dtor = optimize_size;
diff --git a/gcc/c-family/c.opt b/gcc/c-family/c.opt
index 1009defbf16..10e53ea67c9 100644
--- a/gcc/c-family/c.opt
+++ b/gcc/c-family/c.opt
@@ -518,6 +518,15 @@ C++ ObjC++ Var(warn_deprecated_copy, 2) Warning
Mark implicitly-declared copy operations as deprecated if the class has a
user-provided copy operation or destructor.
+Wdeprecated-enum-enum-conversion
+C++ ObjC++ Var(warn_deprecated_enum_enum_conv) Warning
+Warn about deprecated arithmetic conversions on operands of enumeration types.
+
+Wdeprecated-enum-float-conversion
+C++ ObjC++ Var(warn_deprecated_enum_float_conv) Warning
+Warn about deprecated arithmetic conversions on operands where one is of enumeration
+type and the other is of a floating-point type.
+
Wdesignated-init
C ObjC Var(warn_designated_init) Init(1) Warning
Warn about positional initialization of structs requiring designated initializers.
@@ -559,7 +568,7 @@ C ObjC C++ ObjC++ Var(warn_enum_compare) Init(-1) Warning LangEnabledBy(C ObjC,W
Warn about comparison of different enum types.
Wenum-conversion
-C ObjC Var(warn_enum_conversion) Init(0) Warning LangEnabledBy(C ObjC,Wextra)
+C ObjC C++ ObjC++ Var(warn_enum_conversion) Init(0) Warning LangEnabledBy(C ObjC,Wextra)
Warn about implicit conversion of enum types.
Werror
diff --git a/gcc/cp/call.c b/gcc/cp/call.c
index bd662518958..9861be1f856 100644
--- a/gcc/cp/call.c
+++ b/gcc/cp/call.c
@@ -5643,17 +5643,40 @@ build_conditional_expr_1 (const op_location_t &loc,
"in conditional expression: %qT vs %qT",
arg2_type, arg3_type);
}
- else if (extra_warnings
+ else if ((complain & tf_warning)
+ && warn_deprecated_enum_float_conv
+ && ((TREE_CODE (arg2_type) == ENUMERAL_TYPE
+ && TREE_CODE (arg3_type) == REAL_TYPE)
+ || (TREE_CODE (arg2_type) == REAL_TYPE
+ && TREE_CODE (arg3_type) == ENUMERAL_TYPE)))
+ {
+ if (TREE_CODE (arg2_type) == ENUMERAL_TYPE)
+ warning_at (loc, OPT_Wdeprecated_enum_float_conversion,
+ "conditional expression between enumeration type "
+ "%qT and floating-point type %qT is deprecated",
+ arg2_type, arg3_type);
+ else
+ warning_at (loc, OPT_Wdeprecated_enum_float_conversion,
+ "conditional expression between floating-point "
+ "type %qT and enumeration type %qT is deprecated",
+ arg2_type, arg3_type);
+ }
+ else if ((extra_warnings || warn_enum_conversion)
&& ((TREE_CODE (arg2_type) == ENUMERAL_TYPE
&& !same_type_p (arg3_type, type_promotes_to (arg2_type)))
|| (TREE_CODE (arg3_type) == ENUMERAL_TYPE
&& !same_type_p (arg2_type,
type_promotes_to (arg3_type)))))
- {
- if (complain & tf_warning)
- warning_at (loc, OPT_Wextra, "enumerated and non-enumerated "
- "type in conditional expression");
- }
+ {
+ if (complain & tf_warning)
+ {
+ enum opt_code opt = (warn_enum_conversion
+ ? OPT_Wenum_conversion
+ : OPT_Wextra);
+ warning_at (loc, opt, "enumerated and "
+ "non-enumerated type in conditional expression");
+ }
+ }
arg2 = perform_implicit_conversion (result_type, arg2, complain);
arg3 = perform_implicit_conversion (result_type, arg3, complain);
diff --git a/gcc/cp/typeck.c b/gcc/cp/typeck.c
index 48d34f1132a..7305310ecbe 100644
--- a/gcc/cp/typeck.c
+++ b/gcc/cp/typeck.c
@@ -4428,6 +4428,104 @@ warn_for_null_address (location_t location, tree op, tsubst_flags_t complain)
}
}
+/* Warn about [expr.arith.conv]/2: If one operand is of enumeration type and
+ the other operand is of a different enumeration type or a floating-point
+ type, this behavior is deprecated ([depr.arith.conv.enum]). CODE is the
+ code of the binary operation, TYPE0 and TYPE1 are the types of the operands,
+ and LOC is the location for the whole binary expression.
+ TODO: Consider combining this with -Wenum-compare in build_new_op_1. */
+
+static void
+do_warn_enum_conversions (location_t loc, enum tree_code code, tree type0,
+ tree type1)
+{
+ if (TREE_CODE (type0) == ENUMERAL_TYPE
+ && TREE_CODE (type1) == ENUMERAL_TYPE
+ && TYPE_MAIN_VARIANT (type0) != TYPE_MAIN_VARIANT (type1))
+ {
+ /* In C++20, -Wdeprecated-enum-enum-conversion is on by default.
+ Otherwise, warn if -Wenum-conversion is on. */
+ enum opt_code opt;
+ if (warn_deprecated_enum_enum_conv)
+ opt = OPT_Wdeprecated_enum_enum_conversion;
+ else if (warn_enum_conversion)
+ opt = OPT_Wenum_conversion;
+ else
+ return;
+
+ switch (code)
+ {
+ case GT_EXPR:
+ case LT_EXPR:
+ case GE_EXPR:
+ case LE_EXPR:
+ case EQ_EXPR:
+ case NE_EXPR:
+ /* Comparisons are handled by -Wenum-compare. */
+ return;
+ case SPACESHIP_EXPR:
+ /* This is invalid, don't warn. */
+ return;
+ case BIT_AND_EXPR:
+ case BIT_IOR_EXPR:
+ case BIT_XOR_EXPR:
+ warning_at (loc, opt, "bitwise operation between different "
+ "enumeration types %qT and %qT is deprecated",
+ type0, type1);
+ return;
+ default:
+ warning_at (loc, opt, "arithmetic between different enumeration "
+ "types %qT and %qT is deprecated", type0, type1);
+ return;
+ }
+ }
+ else if ((TREE_CODE (type0) == ENUMERAL_TYPE
+ && TREE_CODE (type1) == REAL_TYPE)
+ || (TREE_CODE (type0) == REAL_TYPE
+ && TREE_CODE (type1) == ENUMERAL_TYPE))
+ {
+ const bool enum_first_p = TREE_CODE (type0) == ENUMERAL_TYPE;
+ /* In C++20, -Wdeprecated-enum-float-conversion is on by default.
+ Otherwise, warn if -Wenum-conversion is on. */
+ enum opt_code opt;
+ if (warn_deprecated_enum_float_conv)
+ opt = OPT_Wdeprecated_enum_float_conversion;
+ else if (warn_enum_conversion)
+ opt = OPT_Wenum_conversion;
+ else
+ return;
+
+ switch (code)
+ {
+ case GT_EXPR:
+ case LT_EXPR:
+ case GE_EXPR:
+ case LE_EXPR:
+ case EQ_EXPR:
+ case NE_EXPR:
+ if (enum_first_p)
+ warning_at (loc, opt, "comparison of enumeration type %qT with "
+ "floating-point type %qT is deprecated",
+ type0, type1);
+ else
+ warning_at (loc, opt, "comparison of floating-point type %qT "
+ "with enumeration type %qT is deprecated",
+ type0, type1);
+ return;
+ default:
+ if (enum_first_p)
+ warning_at (loc, opt, "arithmetic between enumeration type %qT "
+ "and floating-point type %qT is deprecated",
+ type0, type1);
+ else
+ warning_at (loc, opt, "arithmetic between floating-point type %qT "
+ "and enumeration type %qT is deprecated",
+ type0, type1);
+ return;
+ }
+ }
+}
+
/* Build a binary-operation expression without default conversions.
CODE is the kind of expression to build.
LOCATION is the location_t of the operator in the source code.
@@ -5445,11 +5543,15 @@ cp_build_binary_op (const op_location_t &location,
{
result_type = cp_common_type (type0, type1);
if (complain & tf_warning)
- do_warn_double_promotion (result_type, type0, type1,
- "implicit conversion from %qH to %qI "
- "to match other operand of binary "
- "expression",
- location);
+ {
+ do_warn_double_promotion (result_type, type0, type1,
+ "implicit conversion from %qH to %qI "
+ "to match other operand of binary "
+ "expression",
+ location);
+ do_warn_enum_conversions (location, code, TREE_TYPE (orig_op0),
+ TREE_TYPE (orig_op1));
+ }
}
if (code == SPACESHIP_EXPR)
diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi
index f82eeea097a..72ae4a23203 100644
--- a/gcc/doc/invoke.texi
+++ b/gcc/doc/invoke.texi
@@ -239,6 +239,7 @@ in the following sections.
-Wno-conversion-null -Wctad-maybe-unsupported @gol
-Wctor-dtor-privacy -Wno-delete-incomplete @gol
-Wdelete-non-virtual-dtor -Wdeprecated-copy -Wdeprecated-copy-dtor @gol
+-Wno-deprecated-enum-enum-conversion -Wno-deprecated-enum-float-conversion @gol
-Weffc++ -Wextra-semi -Wno-inaccessible-base @gol
-Wno-inherited-variadic-ctor -Wno-init-list-lifetime @gol
-Wno-invalid-offsetof -Wno-literal-suffix -Wmismatched-tags @gol
@@ -3358,6 +3359,42 @@ warning is enabled by @option{-Wextra}. With
@option{-Wdeprecated-copy-dtor}, also deprecate if the class has a
user-provided destructor.
+@item -Wno-deprecated-enum-enum-conversion @r{(C++ and Objective-C++ only)}
+@opindex Wdeprecated-enum-enum-conversion
+@opindex Wno-deprecated-enum-enum-conversion
+Disable the warning about the case when the usual arithmetic conversions
+are applied on operands where one is of enumeration type and the other is
+of a different enumeration type. This conversion was deprecated in C++20.
+For example:
+
+@smallexample
+enum E1 @{ e @};
+enum E2 @{ f @};
+int k = f - e;
+@end smallexample
+
+@option{-Wdeprecated-enum-enum-conversion} is enabled by default with
+@option{-std=c++20}. In pre-C++20 dialects, this warning can be enabled
+by @option{-Wenum-conversion}.
+
+@item -Wno-deprecated-enum-float-conversion @r{(C++ and Objective-C++ only)}
+@opindex Wdeprecated-enum-float-conversion
+@opindex Wno-deprecated-enum-float-conversion
+Disable the warning about the case when the usual arithmetic conversions
+are applied on operands where one is of enumeration type and the other is
+of a floating-point type. This conversion was deprecated in C++20. For
+example:
+
+@smallexample
+enum E1 @{ e @};
+enum E2 @{ f @};
+bool b = e <= 3.7;
+@end smallexample
+
+@option{-Wdeprecated-enum-float-conversion} is enabled by default with
+@option{-std=c++20}. In pre-C++20 dialects, this warning can be enabled
+by @option{-Wenum-conversion}.
+
@item -Wno-init-list-lifetime @r{(C++ and Objective-C++ only)}
@opindex Winit-list-lifetime
@opindex Wno-init-list-lifetime
@@ -5271,7 +5308,6 @@ Options} and @ref{Objective-C and Objective-C++ Dialect Options}.
-Wcomment @gol
-Wduplicate-decl-specifier @r{(C and Objective-C only)} @gol
-Wenum-compare @r{(in C/ObjC; this is on by default in C++)} @gol
--Wenum-conversion @r{in C/ObjC;} @gol
-Wformat @gol
-Wformat-overflow @gol
-Wformat-truncation @gol
@@ -5340,6 +5376,7 @@ name is still supported, but the newer name is more descriptive.)
-Wcast-function-type @gol
-Wdeprecated-copy @r{(C++ only)} @gol
-Wempty-body @gol
+-Wenum-conversion @r{(C only)} @gol
-Wignored-qualifiers @gol
-Wimplicit-fallthrough=3 @gol
-Wmissing-field-initializers @gol
@@ -8006,11 +8043,12 @@ In C++ enumerated type mismatches in conditional expressions are also
diagnosed and the warning is enabled by default. In C this warning is
enabled by @option{-Wall}.
-@item -Wenum-conversion @r{(C, Objective-C only)}
+@item -Wenum-conversion
@opindex Wenum-conversion
@opindex Wno-enum-conversion
Warn when a value of enumerated type is implicitly converted to a
-different enumerated type. This warning is enabled by @option{-Wextra}.
+different enumerated type. This warning is enabled by @option{-Wextra}
+in C@.
@item -Wjump-misses-init @r{(C, Objective-C only)}
@opindex Wjump-misses-init
diff --git a/gcc/testsuite/g++.dg/cpp0x/linkage2.C b/gcc/testsuite/g++.dg/cpp0x/linkage2.C
index 52858687ed3..549bd825aab 100644
--- a/gcc/testsuite/g++.dg/cpp0x/linkage2.C
+++ b/gcc/testsuite/g++.dg/cpp0x/linkage2.C
@@ -29,5 +29,5 @@ void f() {
ba.g(a); // OK
ba.h(a); // error, B<T>::h never defined
i(ba, a); // OK
- e1+e2+e3;
+ e1+e2+e3; // { dg-warning "arithmetic between different enumeration types" "" { target c++20 } }
}
diff --git a/gcc/testsuite/g++.dg/cpp2a/enum-conv1.C b/gcc/testsuite/g++.dg/cpp2a/enum-conv1.C
new file mode 100644
index 00000000000..d4960f334dd
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp2a/enum-conv1.C
@@ -0,0 +1,120 @@
+// PR c++/97573
+// { dg-do compile }
+// No special options. In C++20 (only), we should get the deprecated warnings
+// by default. -Wenum-compare is enabled by default so some of them will be
+// printed even pre-C++20.
+
+enum E1 { e } e1;
+enum E2 { f } e2;
+__extension__ static enum { } u1;
+__extension__ static enum { } u2;
+static double d;
+
+void
+conv ()
+{
+ bool b1 = e == e1;
+ bool b2 = e == f; // { dg-warning "comparison between .enum E1. and .enum E2." }
+ bool b3 = e == 0.0; // { dg-warning "comparison of enumeration type .E1. with floating-point type .double." "" { target c++20 } }
+ bool b4 = 0.0 == f; // { dg-warning "comparison of floating-point type .double. with enumeration type .E2." "" { target c++20 } }
+ int n1 = true ? e : f; // { dg-warning "enumerated mismatch" }
+ int n2 = true ? e : 0.0; // { dg-warning "conditional expression between" "" { target c++20 } }
+}
+
+int
+enum_enum (bool b)
+{
+ int r = 0;
+ const E1 e1c = e;
+
+ r += e - e;
+ r += e - e1;
+ r += e - f; // { dg-warning "arithmetic between different enumeration types .E1. and .E2." "" { target c++20 } }
+ r += f - e; // { dg-warning "arithmetic between different enumeration types .E2. and .E1." "" { target c++20 } }
+
+ r += f + f;
+ r += f + e; // { dg-warning "arithmetic between different enumeration types .E2. and .E1." "" { target c++20 } }
+ r += e + f; // { dg-warning "arithmetic between different enumeration types .E1. and .E2." "" { target c++20 } }
+
+ r += e1 - e2; // { dg-warning "arithmetic between different enumeration types .E1. and .E2." "" { target c++20 } }
+ r += e1 - e1c;
+ r += e1c - e1;
+
+ r += e * f; // { dg-warning "arithmetic between different enumeration types .E1. and .E2." "" { target c++20 } }
+ r += f * e; // { dg-warning "arithmetic between different enumeration types .E2. and .E1." "" { target c++20 } }
+ r += e * e;
+
+ r += e1 < e1c;
+ r += e < e1;
+ r += e1 < e2; // { dg-warning "comparison between .enum E1. and .enum E2." }
+ r += e < f; // { dg-warning "comparison between .enum E1. and .enum E2." }
+ r += f < e; // { dg-warning "comparison between .enum E2. and .enum E1." }
+
+ r += e1 == e1c;
+ r += e == e1;
+ r += e == f; // { dg-warning "comparison between .enum E1. and .enum E2." }
+ r += f == e; // { dg-warning "comparison between .enum E2. and .enum E1." }
+ r += e1 == e2; // { dg-warning "comparison between .enum E1. and .enum E2." }
+ r += e2 == e1; // { dg-warning "comparison between .enum E2. and .enum E1." }
+
+ r += b ? e1 : e1c;
+ r += b ? e1 : e;
+ r += b ? f : e; // { dg-warning "enumerated mismatch in conditional expression: .E2. vs .E1." }
+ r += b ? e1 : e2; // { dg-warning "enumerated mismatch in conditional expression: .E1. vs .E2." }
+
+ r += e | f; // { dg-warning "bitwise operation between different enumeration types .E1. and .E2." "" { target c++20 } }
+ r += e ^ f; // { dg-warning "bitwise operation between different enumeration types .E1. and .E2." "" { target c++20 } }
+ r += e & f; // { dg-warning "bitwise operation between different enumeration types .E1. and .E2." "" { target c++20 } }
+ r += !e;
+ r += e1 | e;
+
+ r += e << f;
+ r += e >> f;
+ r += e || f;
+ r += e && f;
+ e1 = e1c;
+
+ // Anonymous enum.
+ r += u1 - u1;
+ r += u1 + u2; // { dg-warning "arithmetic between different enumeration types" "" { target c++20 } }
+ r += u1 * u2; // { dg-warning "arithmetic between different enumeration types" "" { target c++20 } }
+ r += u1 == u2; // { dg-warning "comparison between" }
+ r += u1 & u2; // { dg-warning "bitwise operation between different enumeration types" "" { target c++20 } }
+
+ return r;
+}
+
+double
+enum_float (bool b)
+{
+ double r = 0.0;
+
+ r += e1 - d; // { dg-warning "arithmetic between enumeration type .E1. and floating-point type .double." "" { target c++20 } }
+ r += d - e1; // { dg-warning "arithmetic between floating-point type .double. and enumeration type .E1." "" { target c++20 } }
+ r += e1 + d; // { dg-warning "arithmetic between enumeration type .E1. and floating-point type .double." "" { target c++20 } }
+ r += d + e1; // { dg-warning "arithmetic between floating-point type .double. and enumeration type .E1." "" { target c++20 } }
+ r += e1 * d; // { dg-warning "arithmetic between enumeration type .E1. and floating-point type .double." "" { target c++20 } }
+ r += d * e1; // { dg-warning "arithmetic between floating-point type .double. and enumeration type .E1." "" { target c++20 } }
+ r += u1 * d; // { dg-warning "arithmetic between enumeration type" "" { target c++20 } }
+ r += d * u1; // { dg-warning "arithmetic between floating-point type" "" { target c++20 } }
+
+ r += e1 < d; // { dg-warning "comparison of enumeration type .E1. with floating-point type .double." "" { target c++20 } }
+ r += d < e1; // { dg-warning "comparison of floating-point type .double. with enumeration type .E1." "" { target c++20 } }
+ r += d == e1; // { dg-warning "comparison of floating-point type .double. with enumeration type .E1." "" { target c++20 } }
+ r += e1 == d; // { dg-warning "comparison of enumeration type .E1. with floating-point type .double." "" { target c++20 } }
+ r += u1 == d; // { dg-warning "comparison of enumeration type" "" { target c++20 } }
+ r += d == u1; // { dg-warning "comparison of floating-point type" "" { target c++20 } }
+
+ r += b ? e1 : d; // { dg-warning "conditional expression between enumeration type .E1. and floating-point type .double." "" { target c++20 } }
+ r += b ? d : e1; // { dg-warning "conditional expression between floating-point type .double. and enumeration type .E1." "" { target c++20 } }
+ r += b ? d : u1; // { dg-warning "conditional expression between" "" { target c++20 } }
+ r += b ? u1 : d; // { dg-warning "conditional expression between" "" { target c++20 } }
+
+ // FIXME should be error
+ // e1 <=> d;
+
+ d += e1; // { dg-warning "arithmetic between floating-point type .double. and enumeration type .E1." "" { target c++20 } }
+ d = e1;
+
+ return r;
+}
diff --git a/gcc/testsuite/g++.dg/cpp2a/enum-conv2.C b/gcc/testsuite/g++.dg/cpp2a/enum-conv2.C
new file mode 100644
index 00000000000..f15827bda14
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp2a/enum-conv2.C
@@ -0,0 +1,115 @@
+// PR c++/97573
+// { dg-do compile { target c++20 } }
+// { dg-options "-Wno-deprecated -Wno-enum-compare" }
+
+enum E1 { e } e1;
+enum E2 { f } e2;
+__extension__ static enum { } u1;
+__extension__ static enum { } u2;
+static double d;
+
+void
+conv ()
+{
+ bool b1 = e == e1;
+ bool b2 = e == f;
+ bool b3 = e == 0.0;
+ bool b4 = 0.0 == f;
+ int n1 = true ? e : f;
+ int n2 = true ? e : 0.0;
+}
+
+int
+enum_enum (bool b)
+{
+ int r = 0;
+ const E1 e1c = e;
+
+ r += e - e;
+ r += e - e1;
+ r += e - f;
+ r += f - e;
+
+ r += f + f;
+ r += f + e;
+ r += e + f;
+
+ r += e1 - e2;
+ r += e1 - e1c;
+ r += e1c - e1;
+
+ r += e * f;
+ r += f * e;
+ r += e * e;
+
+ r += e1 < e1c;
+ r += e < e1;
+ r += e1 < e2;
+ r += e < f;
+ r += f < e;
+
+ r += e1 == e1c;
+ r += e == e1;
+ r += e == f;
+ r += f == e;
+ r += e1 == e2;
+ r += e2 == e1;
+
+ r += b ? e1 : e1c;
+ r += b ? e1 : e;
+ r += b ? f : e;
+ r += b ? e1 : e2;
+
+ r += e | f;
+ r += e ^ f;
+ r += e & f;
+ r += !e;
+ r += e1 | e;
+
+ r += e << f;
+ r += e >> f;
+ r += e || f;
+ r += e && f;
+ e1 = e1c;
+
+ // Anonymous enum.
+ r += u1 - u1;
+ r += u1 + u2;
+ r += u1 * u2;
+ r += u1 == u2;
+ r += u1 & u2;
+
+ return r;
+}
+
+double
+enum_float (bool b)
+{
+ double r = 0.0;
+
+ r += e1 - d;
+ r += d - e1;
+ r += e1 + d;
+ r += d + e1;
+ r += e1 * d;
+ r += d * e1;
+ r += u1 * d;
+ r += d * u1;
+
+ r += e1 < d;
+ r += d < e1;
+ r += d == e1;
+ r += e1 == d;
+ r += u1 == d;
+ r += d == u1;
+
+ r += b ? e1 : d;
+ r += b ? d : e1;
+ r += b ? d : u1;
+ r += b ? u1 : d;
+
+ d += e1;
+ d = e1;
+
+ return r;
+}
diff --git a/gcc/testsuite/g++.dg/cpp2a/enum-conv3.C b/gcc/testsuite/g++.dg/cpp2a/enum-conv3.C
new file mode 100644
index 00000000000..67bdf1600d7
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp2a/enum-conv3.C
@@ -0,0 +1,115 @@
+// PR c++/97573
+// { dg-do compile { target { c++17_down } } }
+// { dg-options "-Wenum-conversion" }
+
+enum E1 { e } e1;
+enum E2 { f } e2;
+__extension__ static enum { } u1;
+__extension__ static enum { } u2;
+static double d;
+
+void
+conv ()
+{
+ bool b1 = e == e1;
+ bool b2 = e == f; // { dg-warning "comparison between .enum E1. and .enum E2." }
+ bool b3 = e == 0.0; // { dg-warning "comparison of enumeration type .E1. with floating-point type .double." }
+ bool b4 = 0.0 == f; // { dg-warning "comparison of floating-point type .double. with enumeration type .E2." }
+ int n1 = true ? e : f; // { dg-warning "enumerated mismatch" }
+ int n2 = true ? e : 0.0; // { dg-warning "enumerated and non-enumerated type in conditional expression" }
+}
+
+int
+enum_enum (bool b)
+{
+ int r = 0;
+ const E1 e1c = e;
+
+ r += e - e;
+ r += e - e1;
+ r += e - f; // { dg-warning "arithmetic between different enumeration types .E1. and .E2." }
+ r += f - e; // { dg-warning "arithmetic between different enumeration types .E2. and .E1." }
+
+ r += f + f;
+ r += f + e; // { dg-warning "arithmetic between different enumeration types .E2. and .E1." }
+ r += e + f; // { dg-warning "arithmetic between different enumeration types .E1. and .E2." }
+
+ r += e1 - e2; // { dg-warning "arithmetic between different enumeration types .E1. and .E2." }
+ r += e1 - e1c;
+ r += e1c - e1;
+
+ r += e * f; // { dg-warning "arithmetic between different enumeration types .E1. and .E2." }
+ r += f * e; // { dg-warning "arithmetic between different enumeration types .E2. and .E1." }
+ r += e * e;
+
+ r += e1 < e1c;
+ r += e < e1;
+ r += e1 < e2; // { dg-warning "comparison between .enum E1. and .enum E2." }
+ r += e < f; // { dg-warning "comparison between .enum E1. and .enum E2." }
+ r += f < e; // { dg-warning "comparison between .enum E2. and .enum E1." }
+
+ r += e1 == e1c;
+ r += e == e1;
+ r += e == f; // { dg-warning "comparison between .enum E1. and .enum E2." }
+ r += f == e; // { dg-warning "comparison between .enum E2. and .enum E1." }
+ r += e1 == e2; // { dg-warning "comparison between .enum E1. and .enum E2." }
+ r += e2 == e1; // { dg-warning "comparison between .enum E2. and .enum E1." }
+
+ r += b ? e1 : e1c;
+ r += b ? e1 : e;
+ r += b ? f : e; // { dg-warning "enumerated mismatch in conditional expression: .E2. vs .E1." }
+ r += b ? e1 : e2; // { dg-warning "enumerated mismatch in conditional expression: .E1. vs .E2." }
+
+ r += e | f; // { dg-warning "bitwise operation between different enumeration types .E1. and .E2." }
+ r += e ^ f; // { dg-warning "bitwise operation between different enumeration types .E1. and .E2." }
+ r += e & f; // { dg-warning "bitwise operation between different enumeration types .E1. and .E2." }
+ r += !e;
+ r += e1 | e;
+
+ r += e << f;
+ r += e >> f;
+ r += e || f;
+ r += e && f;
+ e1 = e1c;
+
+ // Anonymous enum.
+ r += u1 - u1;
+ r += u1 + u2; // { dg-warning "arithmetic between different enumeration types" }
+ r += u1 * u2; // { dg-warning "arithmetic between different enumeration types" }
+ r += u1 == u2; // { dg-warning "comparison between" }
+ r += u1 & u2; // { dg-warning "bitwise operation between different enumeration types" }
+
+ return r;
+}
+
+double
+enum_float (bool b)
+{
+ double r = 0.0;
+
+ r += e1 - d; // { dg-warning "arithmetic between enumeration type .E1. and floating-point type .double." }
+ r += d - e1; // { dg-warning "arithmetic between floating-point type .double. and enumeration type .E1." }
+ r += e1 + d; // { dg-warning "arithmetic between enumeration type .E1. and floating-point type .double." }
+ r += d + e1; // { dg-warning "arithmetic between floating-point type .double. and enumeration type .E1." }
+ r += e1 * d; // { dg-warning "arithmetic between enumeration type .E1. and floating-point type .double." }
+ r += d * e1; // { dg-warning "arithmetic between floating-point type .double. and enumeration type .E1." }
+ r += u1 * d; // { dg-warning "arithmetic between enumeration type" }
+ r += d * u1; // { dg-warning "arithmetic between floating-point type" }
+
+ r += e1 < d; // { dg-warning "comparison of enumeration type .E1. with floating-point type .double." }
+ r += d < e1; // { dg-warning "comparison of floating-point type .double. with enumeration type .E1." }
+ r += d == e1; // { dg-warning "comparison of floating-point type .double. with enumeration type .E1." }
+ r += e1 == d; // { dg-warning "comparison of enumeration type .E1. with floating-point type .double." }
+ r += u1 == d; // { dg-warning "comparison of enumeration type" }
+ r += d == u1; // { dg-warning "comparison of floating-point type" }
+
+ r += b ? e1 : d; // { dg-warning "enumerated and non-enumerated type in conditional expression" }
+ r += b ? d : e1; // { dg-warning "enumerated and non-enumerated type in conditional expression" }
+ r += b ? d : u1; // { dg-warning "enumerated and non-enumerated type in conditional expression" }
+ r += b ? u1 : d; // { dg-warning "enumerated and non-enumerated type in conditional expression" }
+
+ d += e1; // { dg-warning "arithmetic between floating-point type .double. and enumeration type .E1." }
+ d = e1;
+
+ return r;
+}
diff --git a/gcc/testsuite/g++.dg/parse/attr3.C b/gcc/testsuite/g++.dg/parse/attr3.C
index 57fa60e130e..de095988015 100644
--- a/gcc/testsuite/g++.dg/parse/attr3.C
+++ b/gcc/testsuite/g++.dg/parse/attr3.C
@@ -10,5 +10,5 @@ int main () {
S::F y; // { dg-warning "'F' is deprecated" }
y = S::f;
- return x + y;
+ return x + y; // { dg-warning "arithmetic between different enumeration types" "" { target c++20 } }
}
base-commit: 75ce04fba49eb30b6a8fe23bc3605cf0ef9a8e28
--
2.26.2
^ permalink raw reply [flat|nested] 4+ messages in thread
* Re: [PATCH] c++: Deprecate arithmetic convs on different enums [PR97573]
2020-10-28 18:01 [PATCH] c++: Deprecate arithmetic convs on different enums [PR97573] Marek Polacek
@ 2020-10-28 18:43 ` Jason Merrill
2020-10-29 2:46 ` Marek Polacek
0 siblings, 1 reply; 4+ messages in thread
From: Jason Merrill @ 2020-10-28 18:43 UTC (permalink / raw)
To: Marek Polacek, GCC Patches
On 10/28/20 2:01 PM, Marek Polacek wrote:
> I noticed that C++20 P1120R0 deprecated certain arithmetic conversions
> as outlined in [depr.arith.conv.enum], but we don't warn about them. In
> particular, "If one operand is of enumeration type and the other operand
> is of a different enumeration type or a floating-point type, this
> behavior is deprecated." These will likely become ill-formed in C++23,
> so we should warn by default in C++20. To this effect, this patch adds
> two new warnings (like clang++): -Wdeprecated-enum-enum-conversion and
> -Wdeprecated-enum-float-conversion. They are enabled by default in
> C++20. In older dialects, to enable these warnings you can now use
> -Wenum-conversion which I made available in C++ too. Note that unlike
> C, in C++ it is not enabled by -Wextra, because that breaks bootstrap.
>
> We already warn about comparisons of two different enumeration types via
> -Wenum-compare, the rest is handled in this patch: we're performing the
> usual arithmetic conversions in these contexts:
> - an arithmetic operation,
> - a bitwise operation,
> - a comparison,
> - a conditional operator,
> - a compound assign operator.
>
> Using the spaceship operator as enum <=> real_type is ill-formed but we
> don't reject it yet.
Hmm, oops. Will you fix that as well? It should be simple to fix in
the SPACESHIP_EXPR block that starts just at the end of this patch.
> We should also address [depr.array.comp] too, but
> it's not handled in this patch.
>
> Bootstrapped/regtested on x86_64-pc-linux-gnu, ok for trunk?
OK, thanks.
> gcc/c-family/ChangeLog:
>
> PR c++/97573
> * c-opts.c (c_common_post_options): In C++20, turn on
> -Wdeprecated-enum-enum-conversion and
> -Wdeprecated-enum-float-conversion.
> * c.opt (Wdeprecated-enum-enum-conversion,
> Wdeprecated-enum-float-conversion): New options.
> (Wenum-conversion): Allow for C++ too.
>
> gcc/cp/ChangeLog:
>
> PR c++/97573
> * call.c (build_conditional_expr_1): Warn about the deprecated
> enum/real type conversion in C++20. Also warn about a non-enumerated
> and enumerated type in ?: when -Wenum-conversion is on.
> * typeck.c (do_warn_enum_conversions): New function.
> (cp_build_binary_op): Call it.
>
> gcc/ChangeLog:
>
> PR c++/97573
> * doc/invoke.texi: Document -Wdeprecated-enum-enum-conversion
> and -Wdeprecated-enum-float-conversion. -Wenum-conversion is
> no longer C/ObjC only.
>
> gcc/testsuite/ChangeLog:
>
> PR c++/97573
> * g++.dg/cpp0x/linkage2.C: Add dg-warning.
> * g++.dg/parse/attr3.C: Likewise.
> * g++.dg/cpp2a/enum-conv1.C: New test.
> * g++.dg/cpp2a/enum-conv2.C: New test.
> * g++.dg/cpp2a/enum-conv3.C: New test.
> ---
> gcc/c-family/c-opts.c | 10 ++
> gcc/c-family/c.opt | 11 ++-
> gcc/cp/call.c | 35 +++++--
> gcc/cp/typeck.c | 112 +++++++++++++++++++++-
> gcc/doc/invoke.texi | 44 ++++++++-
> gcc/testsuite/g++.dg/cpp0x/linkage2.C | 2 +-
> gcc/testsuite/g++.dg/cpp2a/enum-conv1.C | 120 ++++++++++++++++++++++++
> gcc/testsuite/g++.dg/cpp2a/enum-conv2.C | 115 +++++++++++++++++++++++
> gcc/testsuite/g++.dg/cpp2a/enum-conv3.C | 115 +++++++++++++++++++++++
> gcc/testsuite/g++.dg/parse/attr3.C | 2 +-
> 10 files changed, 549 insertions(+), 17 deletions(-)
> create mode 100644 gcc/testsuite/g++.dg/cpp2a/enum-conv1.C
> create mode 100644 gcc/testsuite/g++.dg/cpp2a/enum-conv2.C
> create mode 100644 gcc/testsuite/g++.dg/cpp2a/enum-conv3.C
>
> diff --git a/gcc/c-family/c-opts.c b/gcc/c-family/c-opts.c
> index 38d33849423..120f4489f6c 100644
> --- a/gcc/c-family/c-opts.c
> +++ b/gcc/c-family/c-opts.c
> @@ -925,6 +925,16 @@ c_common_post_options (const char **pfilename)
> SET_OPTION_IF_UNSET (&global_options, &global_options_set, warn_volatile,
> cxx_dialect >= cxx20 && warn_deprecated);
>
> + /* -Wdeprecated-enum-enum-conversion is enabled by default in C++20. */
> + SET_OPTION_IF_UNSET (&global_options, &global_options_set,
> + warn_deprecated_enum_enum_conv,
> + cxx_dialect >= cxx20 && warn_deprecated);
> +
> + /* -Wdeprecated-enum-float-conversion is enabled by default in C++20. */
> + SET_OPTION_IF_UNSET (&global_options, &global_options_set,
> + warn_deprecated_enum_float_conv,
> + cxx_dialect >= cxx20 && warn_deprecated);
> +
> /* Declone C++ 'structors if -Os. */
> if (flag_declone_ctor_dtor == -1)
> flag_declone_ctor_dtor = optimize_size;
> diff --git a/gcc/c-family/c.opt b/gcc/c-family/c.opt
> index 1009defbf16..10e53ea67c9 100644
> --- a/gcc/c-family/c.opt
> +++ b/gcc/c-family/c.opt
> @@ -518,6 +518,15 @@ C++ ObjC++ Var(warn_deprecated_copy, 2) Warning
> Mark implicitly-declared copy operations as deprecated if the class has a
> user-provided copy operation or destructor.
>
> +Wdeprecated-enum-enum-conversion
> +C++ ObjC++ Var(warn_deprecated_enum_enum_conv) Warning
> +Warn about deprecated arithmetic conversions on operands of enumeration types.
> +
> +Wdeprecated-enum-float-conversion
> +C++ ObjC++ Var(warn_deprecated_enum_float_conv) Warning
> +Warn about deprecated arithmetic conversions on operands where one is of enumeration
> +type and the other is of a floating-point type.
> +
> Wdesignated-init
> C ObjC Var(warn_designated_init) Init(1) Warning
> Warn about positional initialization of structs requiring designated initializers.
> @@ -559,7 +568,7 @@ C ObjC C++ ObjC++ Var(warn_enum_compare) Init(-1) Warning LangEnabledBy(C ObjC,W
> Warn about comparison of different enum types.
>
> Wenum-conversion
> -C ObjC Var(warn_enum_conversion) Init(0) Warning LangEnabledBy(C ObjC,Wextra)
> +C ObjC C++ ObjC++ Var(warn_enum_conversion) Init(0) Warning LangEnabledBy(C ObjC,Wextra)
> Warn about implicit conversion of enum types.
>
> Werror
> diff --git a/gcc/cp/call.c b/gcc/cp/call.c
> index bd662518958..9861be1f856 100644
> --- a/gcc/cp/call.c
> +++ b/gcc/cp/call.c
> @@ -5643,17 +5643,40 @@ build_conditional_expr_1 (const op_location_t &loc,
> "in conditional expression: %qT vs %qT",
> arg2_type, arg3_type);
> }
> - else if (extra_warnings
> + else if ((complain & tf_warning)
> + && warn_deprecated_enum_float_conv
> + && ((TREE_CODE (arg2_type) == ENUMERAL_TYPE
> + && TREE_CODE (arg3_type) == REAL_TYPE)
> + || (TREE_CODE (arg2_type) == REAL_TYPE
> + && TREE_CODE (arg3_type) == ENUMERAL_TYPE)))
> + {
> + if (TREE_CODE (arg2_type) == ENUMERAL_TYPE)
> + warning_at (loc, OPT_Wdeprecated_enum_float_conversion,
> + "conditional expression between enumeration type "
> + "%qT and floating-point type %qT is deprecated",
> + arg2_type, arg3_type);
> + else
> + warning_at (loc, OPT_Wdeprecated_enum_float_conversion,
> + "conditional expression between floating-point "
> + "type %qT and enumeration type %qT is deprecated",
> + arg2_type, arg3_type);
> + }
> + else if ((extra_warnings || warn_enum_conversion)
> && ((TREE_CODE (arg2_type) == ENUMERAL_TYPE
> && !same_type_p (arg3_type, type_promotes_to (arg2_type)))
> || (TREE_CODE (arg3_type) == ENUMERAL_TYPE
> && !same_type_p (arg2_type,
> type_promotes_to (arg3_type)))))
> - {
> - if (complain & tf_warning)
> - warning_at (loc, OPT_Wextra, "enumerated and non-enumerated "
> - "type in conditional expression");
> - }
> + {
> + if (complain & tf_warning)
> + {
> + enum opt_code opt = (warn_enum_conversion
> + ? OPT_Wenum_conversion
> + : OPT_Wextra);
> + warning_at (loc, opt, "enumerated and "
> + "non-enumerated type in conditional expression");
> + }
> + }
>
> arg2 = perform_implicit_conversion (result_type, arg2, complain);
> arg3 = perform_implicit_conversion (result_type, arg3, complain);
> diff --git a/gcc/cp/typeck.c b/gcc/cp/typeck.c
> index 48d34f1132a..7305310ecbe 100644
> --- a/gcc/cp/typeck.c
> +++ b/gcc/cp/typeck.c
> @@ -4428,6 +4428,104 @@ warn_for_null_address (location_t location, tree op, tsubst_flags_t complain)
> }
> }
>
> +/* Warn about [expr.arith.conv]/2: If one operand is of enumeration type and
> + the other operand is of a different enumeration type or a floating-point
> + type, this behavior is deprecated ([depr.arith.conv.enum]). CODE is the
> + code of the binary operation, TYPE0 and TYPE1 are the types of the operands,
> + and LOC is the location for the whole binary expression.
> + TODO: Consider combining this with -Wenum-compare in build_new_op_1. */
> +
> +static void
> +do_warn_enum_conversions (location_t loc, enum tree_code code, tree type0,
> + tree type1)
> +{
> + if (TREE_CODE (type0) == ENUMERAL_TYPE
> + && TREE_CODE (type1) == ENUMERAL_TYPE
> + && TYPE_MAIN_VARIANT (type0) != TYPE_MAIN_VARIANT (type1))
> + {
> + /* In C++20, -Wdeprecated-enum-enum-conversion is on by default.
> + Otherwise, warn if -Wenum-conversion is on. */
> + enum opt_code opt;
> + if (warn_deprecated_enum_enum_conv)
> + opt = OPT_Wdeprecated_enum_enum_conversion;
> + else if (warn_enum_conversion)
> + opt = OPT_Wenum_conversion;
> + else
> + return;
> +
> + switch (code)
> + {
> + case GT_EXPR:
> + case LT_EXPR:
> + case GE_EXPR:
> + case LE_EXPR:
> + case EQ_EXPR:
> + case NE_EXPR:
> + /* Comparisons are handled by -Wenum-compare. */
> + return;
> + case SPACESHIP_EXPR:
> + /* This is invalid, don't warn. */
> + return;
> + case BIT_AND_EXPR:
> + case BIT_IOR_EXPR:
> + case BIT_XOR_EXPR:
> + warning_at (loc, opt, "bitwise operation between different "
> + "enumeration types %qT and %qT is deprecated",
> + type0, type1);
> + return;
> + default:
> + warning_at (loc, opt, "arithmetic between different enumeration "
> + "types %qT and %qT is deprecated", type0, type1);
> + return;
> + }
> + }
> + else if ((TREE_CODE (type0) == ENUMERAL_TYPE
> + && TREE_CODE (type1) == REAL_TYPE)
> + || (TREE_CODE (type0) == REAL_TYPE
> + && TREE_CODE (type1) == ENUMERAL_TYPE))
> + {
> + const bool enum_first_p = TREE_CODE (type0) == ENUMERAL_TYPE;
> + /* In C++20, -Wdeprecated-enum-float-conversion is on by default.
> + Otherwise, warn if -Wenum-conversion is on. */
> + enum opt_code opt;
> + if (warn_deprecated_enum_float_conv)
> + opt = OPT_Wdeprecated_enum_float_conversion;
> + else if (warn_enum_conversion)
> + opt = OPT_Wenum_conversion;
> + else
> + return;
> +
> + switch (code)
> + {
> + case GT_EXPR:
> + case LT_EXPR:
> + case GE_EXPR:
> + case LE_EXPR:
> + case EQ_EXPR:
> + case NE_EXPR:
> + if (enum_first_p)
> + warning_at (loc, opt, "comparison of enumeration type %qT with "
> + "floating-point type %qT is deprecated",
> + type0, type1);
> + else
> + warning_at (loc, opt, "comparison of floating-point type %qT "
> + "with enumeration type %qT is deprecated",
> + type0, type1);
> + return;
> + default:
> + if (enum_first_p)
> + warning_at (loc, opt, "arithmetic between enumeration type %qT "
> + "and floating-point type %qT is deprecated",
> + type0, type1);
> + else
> + warning_at (loc, opt, "arithmetic between floating-point type %qT "
> + "and enumeration type %qT is deprecated",
> + type0, type1);
> + return;
> + }
> + }
> +}
> +
> /* Build a binary-operation expression without default conversions.
> CODE is the kind of expression to build.
> LOCATION is the location_t of the operator in the source code.
> @@ -5445,11 +5543,15 @@ cp_build_binary_op (const op_location_t &location,
> {
> result_type = cp_common_type (type0, type1);
> if (complain & tf_warning)
> - do_warn_double_promotion (result_type, type0, type1,
> - "implicit conversion from %qH to %qI "
> - "to match other operand of binary "
> - "expression",
> - location);
> + {
> + do_warn_double_promotion (result_type, type0, type1,
> + "implicit conversion from %qH to %qI "
> + "to match other operand of binary "
> + "expression",
> + location);
> + do_warn_enum_conversions (location, code, TREE_TYPE (orig_op0),
> + TREE_TYPE (orig_op1));
> + }
> }
>
> if (code == SPACESHIP_EXPR)
> diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi
> index f82eeea097a..72ae4a23203 100644
> --- a/gcc/doc/invoke.texi
> +++ b/gcc/doc/invoke.texi
> @@ -239,6 +239,7 @@ in the following sections.
> -Wno-conversion-null -Wctad-maybe-unsupported @gol
> -Wctor-dtor-privacy -Wno-delete-incomplete @gol
> -Wdelete-non-virtual-dtor -Wdeprecated-copy -Wdeprecated-copy-dtor @gol
> +-Wno-deprecated-enum-enum-conversion -Wno-deprecated-enum-float-conversion @gol
> -Weffc++ -Wextra-semi -Wno-inaccessible-base @gol
> -Wno-inherited-variadic-ctor -Wno-init-list-lifetime @gol
> -Wno-invalid-offsetof -Wno-literal-suffix -Wmismatched-tags @gol
> @@ -3358,6 +3359,42 @@ warning is enabled by @option{-Wextra}. With
> @option{-Wdeprecated-copy-dtor}, also deprecate if the class has a
> user-provided destructor.
>
> +@item -Wno-deprecated-enum-enum-conversion @r{(C++ and Objective-C++ only)}
> +@opindex Wdeprecated-enum-enum-conversion
> +@opindex Wno-deprecated-enum-enum-conversion
> +Disable the warning about the case when the usual arithmetic conversions
> +are applied on operands where one is of enumeration type and the other is
> +of a different enumeration type. This conversion was deprecated in C++20.
> +For example:
> +
> +@smallexample
> +enum E1 @{ e @};
> +enum E2 @{ f @};
> +int k = f - e;
> +@end smallexample
> +
> +@option{-Wdeprecated-enum-enum-conversion} is enabled by default with
> +@option{-std=c++20}. In pre-C++20 dialects, this warning can be enabled
> +by @option{-Wenum-conversion}.
> +
> +@item -Wno-deprecated-enum-float-conversion @r{(C++ and Objective-C++ only)}
> +@opindex Wdeprecated-enum-float-conversion
> +@opindex Wno-deprecated-enum-float-conversion
> +Disable the warning about the case when the usual arithmetic conversions
> +are applied on operands where one is of enumeration type and the other is
> +of a floating-point type. This conversion was deprecated in C++20. For
> +example:
> +
> +@smallexample
> +enum E1 @{ e @};
> +enum E2 @{ f @};
> +bool b = e <= 3.7;
> +@end smallexample
> +
> +@option{-Wdeprecated-enum-float-conversion} is enabled by default with
> +@option{-std=c++20}. In pre-C++20 dialects, this warning can be enabled
> +by @option{-Wenum-conversion}.
> +
> @item -Wno-init-list-lifetime @r{(C++ and Objective-C++ only)}
> @opindex Winit-list-lifetime
> @opindex Wno-init-list-lifetime
> @@ -5271,7 +5308,6 @@ Options} and @ref{Objective-C and Objective-C++ Dialect Options}.
> -Wcomment @gol
> -Wduplicate-decl-specifier @r{(C and Objective-C only)} @gol
> -Wenum-compare @r{(in C/ObjC; this is on by default in C++)} @gol
> --Wenum-conversion @r{in C/ObjC;} @gol
> -Wformat @gol
> -Wformat-overflow @gol
> -Wformat-truncation @gol
> @@ -5340,6 +5376,7 @@ name is still supported, but the newer name is more descriptive.)
> -Wcast-function-type @gol
> -Wdeprecated-copy @r{(C++ only)} @gol
> -Wempty-body @gol
> +-Wenum-conversion @r{(C only)} @gol
> -Wignored-qualifiers @gol
> -Wimplicit-fallthrough=3 @gol
> -Wmissing-field-initializers @gol
> @@ -8006,11 +8043,12 @@ In C++ enumerated type mismatches in conditional expressions are also
> diagnosed and the warning is enabled by default. In C this warning is
> enabled by @option{-Wall}.
>
> -@item -Wenum-conversion @r{(C, Objective-C only)}
> +@item -Wenum-conversion
> @opindex Wenum-conversion
> @opindex Wno-enum-conversion
> Warn when a value of enumerated type is implicitly converted to a
> -different enumerated type. This warning is enabled by @option{-Wextra}.
> +different enumerated type. This warning is enabled by @option{-Wextra}
> +in C@.
>
> @item -Wjump-misses-init @r{(C, Objective-C only)}
> @opindex Wjump-misses-init
> diff --git a/gcc/testsuite/g++.dg/cpp0x/linkage2.C b/gcc/testsuite/g++.dg/cpp0x/linkage2.C
> index 52858687ed3..549bd825aab 100644
> --- a/gcc/testsuite/g++.dg/cpp0x/linkage2.C
> +++ b/gcc/testsuite/g++.dg/cpp0x/linkage2.C
> @@ -29,5 +29,5 @@ void f() {
> ba.g(a); // OK
> ba.h(a); // error, B<T>::h never defined
> i(ba, a); // OK
> - e1+e2+e3;
> + e1+e2+e3; // { dg-warning "arithmetic between different enumeration types" "" { target c++20 } }
> }
> diff --git a/gcc/testsuite/g++.dg/cpp2a/enum-conv1.C b/gcc/testsuite/g++.dg/cpp2a/enum-conv1.C
> new file mode 100644
> index 00000000000..d4960f334dd
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/cpp2a/enum-conv1.C
> @@ -0,0 +1,120 @@
> +// PR c++/97573
> +// { dg-do compile }
> +// No special options. In C++20 (only), we should get the deprecated warnings
> +// by default. -Wenum-compare is enabled by default so some of them will be
> +// printed even pre-C++20.
> +
> +enum E1 { e } e1;
> +enum E2 { f } e2;
> +__extension__ static enum { } u1;
> +__extension__ static enum { } u2;
> +static double d;
> +
> +void
> +conv ()
> +{
> + bool b1 = e == e1;
> + bool b2 = e == f; // { dg-warning "comparison between .enum E1. and .enum E2." }
> + bool b3 = e == 0.0; // { dg-warning "comparison of enumeration type .E1. with floating-point type .double." "" { target c++20 } }
> + bool b4 = 0.0 == f; // { dg-warning "comparison of floating-point type .double. with enumeration type .E2." "" { target c++20 } }
> + int n1 = true ? e : f; // { dg-warning "enumerated mismatch" }
> + int n2 = true ? e : 0.0; // { dg-warning "conditional expression between" "" { target c++20 } }
> +}
> +
> +int
> +enum_enum (bool b)
> +{
> + int r = 0;
> + const E1 e1c = e;
> +
> + r += e - e;
> + r += e - e1;
> + r += e - f; // { dg-warning "arithmetic between different enumeration types .E1. and .E2." "" { target c++20 } }
> + r += f - e; // { dg-warning "arithmetic between different enumeration types .E2. and .E1." "" { target c++20 } }
> +
> + r += f + f;
> + r += f + e; // { dg-warning "arithmetic between different enumeration types .E2. and .E1." "" { target c++20 } }
> + r += e + f; // { dg-warning "arithmetic between different enumeration types .E1. and .E2." "" { target c++20 } }
> +
> + r += e1 - e2; // { dg-warning "arithmetic between different enumeration types .E1. and .E2." "" { target c++20 } }
> + r += e1 - e1c;
> + r += e1c - e1;
> +
> + r += e * f; // { dg-warning "arithmetic between different enumeration types .E1. and .E2." "" { target c++20 } }
> + r += f * e; // { dg-warning "arithmetic between different enumeration types .E2. and .E1." "" { target c++20 } }
> + r += e * e;
> +
> + r += e1 < e1c;
> + r += e < e1;
> + r += e1 < e2; // { dg-warning "comparison between .enum E1. and .enum E2." }
> + r += e < f; // { dg-warning "comparison between .enum E1. and .enum E2." }
> + r += f < e; // { dg-warning "comparison between .enum E2. and .enum E1." }
> +
> + r += e1 == e1c;
> + r += e == e1;
> + r += e == f; // { dg-warning "comparison between .enum E1. and .enum E2." }
> + r += f == e; // { dg-warning "comparison between .enum E2. and .enum E1." }
> + r += e1 == e2; // { dg-warning "comparison between .enum E1. and .enum E2." }
> + r += e2 == e1; // { dg-warning "comparison between .enum E2. and .enum E1." }
> +
> + r += b ? e1 : e1c;
> + r += b ? e1 : e;
> + r += b ? f : e; // { dg-warning "enumerated mismatch in conditional expression: .E2. vs .E1." }
> + r += b ? e1 : e2; // { dg-warning "enumerated mismatch in conditional expression: .E1. vs .E2." }
> +
> + r += e | f; // { dg-warning "bitwise operation between different enumeration types .E1. and .E2." "" { target c++20 } }
> + r += e ^ f; // { dg-warning "bitwise operation between different enumeration types .E1. and .E2." "" { target c++20 } }
> + r += e & f; // { dg-warning "bitwise operation between different enumeration types .E1. and .E2." "" { target c++20 } }
> + r += !e;
> + r += e1 | e;
> +
> + r += e << f;
> + r += e >> f;
> + r += e || f;
> + r += e && f;
> + e1 = e1c;
> +
> + // Anonymous enum.
> + r += u1 - u1;
> + r += u1 + u2; // { dg-warning "arithmetic between different enumeration types" "" { target c++20 } }
> + r += u1 * u2; // { dg-warning "arithmetic between different enumeration types" "" { target c++20 } }
> + r += u1 == u2; // { dg-warning "comparison between" }
> + r += u1 & u2; // { dg-warning "bitwise operation between different enumeration types" "" { target c++20 } }
> +
> + return r;
> +}
> +
> +double
> +enum_float (bool b)
> +{
> + double r = 0.0;
> +
> + r += e1 - d; // { dg-warning "arithmetic between enumeration type .E1. and floating-point type .double." "" { target c++20 } }
> + r += d - e1; // { dg-warning "arithmetic between floating-point type .double. and enumeration type .E1." "" { target c++20 } }
> + r += e1 + d; // { dg-warning "arithmetic between enumeration type .E1. and floating-point type .double." "" { target c++20 } }
> + r += d + e1; // { dg-warning "arithmetic between floating-point type .double. and enumeration type .E1." "" { target c++20 } }
> + r += e1 * d; // { dg-warning "arithmetic between enumeration type .E1. and floating-point type .double." "" { target c++20 } }
> + r += d * e1; // { dg-warning "arithmetic between floating-point type .double. and enumeration type .E1." "" { target c++20 } }
> + r += u1 * d; // { dg-warning "arithmetic between enumeration type" "" { target c++20 } }
> + r += d * u1; // { dg-warning "arithmetic between floating-point type" "" { target c++20 } }
> +
> + r += e1 < d; // { dg-warning "comparison of enumeration type .E1. with floating-point type .double." "" { target c++20 } }
> + r += d < e1; // { dg-warning "comparison of floating-point type .double. with enumeration type .E1." "" { target c++20 } }
> + r += d == e1; // { dg-warning "comparison of floating-point type .double. with enumeration type .E1." "" { target c++20 } }
> + r += e1 == d; // { dg-warning "comparison of enumeration type .E1. with floating-point type .double." "" { target c++20 } }
> + r += u1 == d; // { dg-warning "comparison of enumeration type" "" { target c++20 } }
> + r += d == u1; // { dg-warning "comparison of floating-point type" "" { target c++20 } }
> +
> + r += b ? e1 : d; // { dg-warning "conditional expression between enumeration type .E1. and floating-point type .double." "" { target c++20 } }
> + r += b ? d : e1; // { dg-warning "conditional expression between floating-point type .double. and enumeration type .E1." "" { target c++20 } }
> + r += b ? d : u1; // { dg-warning "conditional expression between" "" { target c++20 } }
> + r += b ? u1 : d; // { dg-warning "conditional expression between" "" { target c++20 } }
> +
> + // FIXME should be error
> + // e1 <=> d;
> +
> + d += e1; // { dg-warning "arithmetic between floating-point type .double. and enumeration type .E1." "" { target c++20 } }
> + d = e1;
> +
> + return r;
> +}
> diff --git a/gcc/testsuite/g++.dg/cpp2a/enum-conv2.C b/gcc/testsuite/g++.dg/cpp2a/enum-conv2.C
> new file mode 100644
> index 00000000000..f15827bda14
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/cpp2a/enum-conv2.C
> @@ -0,0 +1,115 @@
> +// PR c++/97573
> +// { dg-do compile { target c++20 } }
> +// { dg-options "-Wno-deprecated -Wno-enum-compare" }
> +
> +enum E1 { e } e1;
> +enum E2 { f } e2;
> +__extension__ static enum { } u1;
> +__extension__ static enum { } u2;
> +static double d;
> +
> +void
> +conv ()
> +{
> + bool b1 = e == e1;
> + bool b2 = e == f;
> + bool b3 = e == 0.0;
> + bool b4 = 0.0 == f;
> + int n1 = true ? e : f;
> + int n2 = true ? e : 0.0;
> +}
> +
> +int
> +enum_enum (bool b)
> +{
> + int r = 0;
> + const E1 e1c = e;
> +
> + r += e - e;
> + r += e - e1;
> + r += e - f;
> + r += f - e;
> +
> + r += f + f;
> + r += f + e;
> + r += e + f;
> +
> + r += e1 - e2;
> + r += e1 - e1c;
> + r += e1c - e1;
> +
> + r += e * f;
> + r += f * e;
> + r += e * e;
> +
> + r += e1 < e1c;
> + r += e < e1;
> + r += e1 < e2;
> + r += e < f;
> + r += f < e;
> +
> + r += e1 == e1c;
> + r += e == e1;
> + r += e == f;
> + r += f == e;
> + r += e1 == e2;
> + r += e2 == e1;
> +
> + r += b ? e1 : e1c;
> + r += b ? e1 : e;
> + r += b ? f : e;
> + r += b ? e1 : e2;
> +
> + r += e | f;
> + r += e ^ f;
> + r += e & f;
> + r += !e;
> + r += e1 | e;
> +
> + r += e << f;
> + r += e >> f;
> + r += e || f;
> + r += e && f;
> + e1 = e1c;
> +
> + // Anonymous enum.
> + r += u1 - u1;
> + r += u1 + u2;
> + r += u1 * u2;
> + r += u1 == u2;
> + r += u1 & u2;
> +
> + return r;
> +}
> +
> +double
> +enum_float (bool b)
> +{
> + double r = 0.0;
> +
> + r += e1 - d;
> + r += d - e1;
> + r += e1 + d;
> + r += d + e1;
> + r += e1 * d;
> + r += d * e1;
> + r += u1 * d;
> + r += d * u1;
> +
> + r += e1 < d;
> + r += d < e1;
> + r += d == e1;
> + r += e1 == d;
> + r += u1 == d;
> + r += d == u1;
> +
> + r += b ? e1 : d;
> + r += b ? d : e1;
> + r += b ? d : u1;
> + r += b ? u1 : d;
> +
> + d += e1;
> + d = e1;
> +
> + return r;
> +}
> diff --git a/gcc/testsuite/g++.dg/cpp2a/enum-conv3.C b/gcc/testsuite/g++.dg/cpp2a/enum-conv3.C
> new file mode 100644
> index 00000000000..67bdf1600d7
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/cpp2a/enum-conv3.C
> @@ -0,0 +1,115 @@
> +// PR c++/97573
> +// { dg-do compile { target { c++17_down } } }
> +// { dg-options "-Wenum-conversion" }
> +
> +enum E1 { e } e1;
> +enum E2 { f } e2;
> +__extension__ static enum { } u1;
> +__extension__ static enum { } u2;
> +static double d;
> +
> +void
> +conv ()
> +{
> + bool b1 = e == e1;
> + bool b2 = e == f; // { dg-warning "comparison between .enum E1. and .enum E2." }
> + bool b3 = e == 0.0; // { dg-warning "comparison of enumeration type .E1. with floating-point type .double." }
> + bool b4 = 0.0 == f; // { dg-warning "comparison of floating-point type .double. with enumeration type .E2." }
> + int n1 = true ? e : f; // { dg-warning "enumerated mismatch" }
> + int n2 = true ? e : 0.0; // { dg-warning "enumerated and non-enumerated type in conditional expression" }
> +}
> +
> +int
> +enum_enum (bool b)
> +{
> + int r = 0;
> + const E1 e1c = e;
> +
> + r += e - e;
> + r += e - e1;
> + r += e - f; // { dg-warning "arithmetic between different enumeration types .E1. and .E2." }
> + r += f - e; // { dg-warning "arithmetic between different enumeration types .E2. and .E1." }
> +
> + r += f + f;
> + r += f + e; // { dg-warning "arithmetic between different enumeration types .E2. and .E1." }
> + r += e + f; // { dg-warning "arithmetic between different enumeration types .E1. and .E2." }
> +
> + r += e1 - e2; // { dg-warning "arithmetic between different enumeration types .E1. and .E2." }
> + r += e1 - e1c;
> + r += e1c - e1;
> +
> + r += e * f; // { dg-warning "arithmetic between different enumeration types .E1. and .E2." }
> + r += f * e; // { dg-warning "arithmetic between different enumeration types .E2. and .E1." }
> + r += e * e;
> +
> + r += e1 < e1c;
> + r += e < e1;
> + r += e1 < e2; // { dg-warning "comparison between .enum E1. and .enum E2." }
> + r += e < f; // { dg-warning "comparison between .enum E1. and .enum E2." }
> + r += f < e; // { dg-warning "comparison between .enum E2. and .enum E1." }
> +
> + r += e1 == e1c;
> + r += e == e1;
> + r += e == f; // { dg-warning "comparison between .enum E1. and .enum E2." }
> + r += f == e; // { dg-warning "comparison between .enum E2. and .enum E1." }
> + r += e1 == e2; // { dg-warning "comparison between .enum E1. and .enum E2." }
> + r += e2 == e1; // { dg-warning "comparison between .enum E2. and .enum E1." }
> +
> + r += b ? e1 : e1c;
> + r += b ? e1 : e;
> + r += b ? f : e; // { dg-warning "enumerated mismatch in conditional expression: .E2. vs .E1." }
> + r += b ? e1 : e2; // { dg-warning "enumerated mismatch in conditional expression: .E1. vs .E2." }
> +
> + r += e | f; // { dg-warning "bitwise operation between different enumeration types .E1. and .E2." }
> + r += e ^ f; // { dg-warning "bitwise operation between different enumeration types .E1. and .E2." }
> + r += e & f; // { dg-warning "bitwise operation between different enumeration types .E1. and .E2." }
> + r += !e;
> + r += e1 | e;
> +
> + r += e << f;
> + r += e >> f;
> + r += e || f;
> + r += e && f;
> + e1 = e1c;
> +
> + // Anonymous enum.
> + r += u1 - u1;
> + r += u1 + u2; // { dg-warning "arithmetic between different enumeration types" }
> + r += u1 * u2; // { dg-warning "arithmetic between different enumeration types" }
> + r += u1 == u2; // { dg-warning "comparison between" }
> + r += u1 & u2; // { dg-warning "bitwise operation between different enumeration types" }
> +
> + return r;
> +}
> +
> +double
> +enum_float (bool b)
> +{
> + double r = 0.0;
> +
> + r += e1 - d; // { dg-warning "arithmetic between enumeration type .E1. and floating-point type .double." }
> + r += d - e1; // { dg-warning "arithmetic between floating-point type .double. and enumeration type .E1." }
> + r += e1 + d; // { dg-warning "arithmetic between enumeration type .E1. and floating-point type .double." }
> + r += d + e1; // { dg-warning "arithmetic between floating-point type .double. and enumeration type .E1." }
> + r += e1 * d; // { dg-warning "arithmetic between enumeration type .E1. and floating-point type .double." }
> + r += d * e1; // { dg-warning "arithmetic between floating-point type .double. and enumeration type .E1." }
> + r += u1 * d; // { dg-warning "arithmetic between enumeration type" }
> + r += d * u1; // { dg-warning "arithmetic between floating-point type" }
> +
> + r += e1 < d; // { dg-warning "comparison of enumeration type .E1. with floating-point type .double." }
> + r += d < e1; // { dg-warning "comparison of floating-point type .double. with enumeration type .E1." }
> + r += d == e1; // { dg-warning "comparison of floating-point type .double. with enumeration type .E1." }
> + r += e1 == d; // { dg-warning "comparison of enumeration type .E1. with floating-point type .double." }
> + r += u1 == d; // { dg-warning "comparison of enumeration type" }
> + r += d == u1; // { dg-warning "comparison of floating-point type" }
> +
> + r += b ? e1 : d; // { dg-warning "enumerated and non-enumerated type in conditional expression" }
> + r += b ? d : e1; // { dg-warning "enumerated and non-enumerated type in conditional expression" }
> + r += b ? d : u1; // { dg-warning "enumerated and non-enumerated type in conditional expression" }
> + r += b ? u1 : d; // { dg-warning "enumerated and non-enumerated type in conditional expression" }
> +
> + d += e1; // { dg-warning "arithmetic between floating-point type .double. and enumeration type .E1." }
> + d = e1;
> +
> + return r;
> +}
> diff --git a/gcc/testsuite/g++.dg/parse/attr3.C b/gcc/testsuite/g++.dg/parse/attr3.C
> index 57fa60e130e..de095988015 100644
> --- a/gcc/testsuite/g++.dg/parse/attr3.C
> +++ b/gcc/testsuite/g++.dg/parse/attr3.C
> @@ -10,5 +10,5 @@ int main () {
> S::F y; // { dg-warning "'F' is deprecated" }
> y = S::f;
>
> - return x + y;
> + return x + y; // { dg-warning "arithmetic between different enumeration types" "" { target c++20 } }
> }
>
> base-commit: 75ce04fba49eb30b6a8fe23bc3605cf0ef9a8e28
>
^ permalink raw reply [flat|nested] 4+ messages in thread
* Re: [PATCH] c++: Deprecate arithmetic convs on different enums [PR97573]
2020-10-28 18:43 ` Jason Merrill
@ 2020-10-29 2:46 ` Marek Polacek
2020-10-29 15:19 ` Jason Merrill
0 siblings, 1 reply; 4+ messages in thread
From: Marek Polacek @ 2020-10-29 2:46 UTC (permalink / raw)
To: Jason Merrill; +Cc: GCC Patches
On Wed, Oct 28, 2020 at 02:43:30PM -0400, Jason Merrill wrote:
> On 10/28/20 2:01 PM, Marek Polacek wrote:
> > I noticed that C++20 P1120R0 deprecated certain arithmetic conversions
> > as outlined in [depr.arith.conv.enum], but we don't warn about them. In
> > particular, "If one operand is of enumeration type and the other operand
> > is of a different enumeration type or a floating-point type, this
> > behavior is deprecated." These will likely become ill-formed in C++23,
> > so we should warn by default in C++20. To this effect, this patch adds
> > two new warnings (like clang++): -Wdeprecated-enum-enum-conversion and
> > -Wdeprecated-enum-float-conversion. They are enabled by default in
> > C++20. In older dialects, to enable these warnings you can now use
> > -Wenum-conversion which I made available in C++ too. Note that unlike
> > C, in C++ it is not enabled by -Wextra, because that breaks bootstrap.
> >
> > We already warn about comparisons of two different enumeration types via
> > -Wenum-compare, the rest is handled in this patch: we're performing the
> > usual arithmetic conversions in these contexts:
> > - an arithmetic operation,
> > - a bitwise operation,
> > - a comparison,
> > - a conditional operator,
> > - a compound assign operator.
> >
> > Using the spaceship operator as enum <=> real_type is ill-formed but we
> > don't reject it yet.
>
> Hmm, oops. Will you fix that as well? It should be simple to fix in the
> SPACESHIP_EXPR block that starts just at the end of this patch.
Sure.
Bootstrapped/regtested on x86_64-pc-linux-gnu, ok for trunk?
From 8ae2e45f2dd35510aed3be1ab249b8612e33f00d Mon Sep 17 00:00:00 2001
From: Marek Polacek <polacek@redhat.com>
Date: Wed, 28 Oct 2020 19:02:29 -0400
Subject: [PATCH] c++: Reject float <=> enum.
As [depr.arith.conv.enum] says, these are ill-formed.
gcc/cp/ChangeLog:
* typeck.c (do_warn_enum_conversions): Don't warn for SPACESHIP_EXPR.
(cp_build_binary_op): Reject float <=> enum or enum <=> float. Use
CP_INTEGRAL_TYPE_P instead of INTEGRAL_OR_ENUMERATION_TYPE_P.
gcc/testsuite/ChangeLog:
* g++.dg/cpp2a/enum-conv1.C: Remove unused code.
* g++.dg/cpp2a/spaceship-err5.C: New test.
---
gcc/cp/typeck.c | 13 ++++++++++--
gcc/testsuite/g++.dg/cpp2a/enum-conv1.C | 3 ---
gcc/testsuite/g++.dg/cpp2a/spaceship-err5.C | 23 +++++++++++++++++++++
3 files changed, 34 insertions(+), 5 deletions(-)
create mode 100644 gcc/testsuite/g++.dg/cpp2a/spaceship-err5.C
diff --git a/gcc/cp/typeck.c b/gcc/cp/typeck.c
index 7305310ecbe..d3b701610cf 100644
--- a/gcc/cp/typeck.c
+++ b/gcc/cp/typeck.c
@@ -4512,6 +4512,9 @@ do_warn_enum_conversions (location_t loc, enum tree_code code, tree type0,
"with enumeration type %qT is deprecated",
type0, type1);
return;
+ case SPACESHIP_EXPR:
+ /* This is invalid, don't warn. */
+ return;
default:
if (enum_first_p)
warning_at (loc, opt, "arithmetic between enumeration type %qT "
@@ -5584,6 +5587,12 @@ cp_build_binary_op (const op_location_t &location,
arithmetic conversions are applied to the operands." So we don't do
arithmetic conversions if the operands both have enumeral type. */
result_type = NULL_TREE;
+ else if ((orig_code0 == ENUMERAL_TYPE && orig_code1 == REAL_TYPE)
+ || (orig_code0 == REAL_TYPE && orig_code1 == ENUMERAL_TYPE))
+ /* [depr.arith.conv.enum]: Three-way comparisons between such operands
+ [where one is of enumeration type and the other is of a different
+ enumeration type or a floating-point type] are ill-formed. */
+ result_type = NULL_TREE;
if (result_type)
{
@@ -5598,12 +5607,12 @@ cp_build_binary_op (const op_location_t &location,
type to a floating point type, the program is ill-formed. */
bool ok = true;
if (TREE_CODE (result_type) == REAL_TYPE
- && INTEGRAL_OR_ENUMERATION_TYPE_P (TREE_TYPE (orig_op0)))
+ && CP_INTEGRAL_TYPE_P (orig_type0))
/* OK */;
else if (!check_narrowing (result_type, orig_op0, complain))
ok = false;
if (TREE_CODE (result_type) == REAL_TYPE
- && INTEGRAL_OR_ENUMERATION_TYPE_P (TREE_TYPE (orig_op1)))
+ && CP_INTEGRAL_TYPE_P (orig_type1))
/* OK */;
else if (!check_narrowing (result_type, orig_op1, complain))
ok = false;
diff --git a/gcc/testsuite/g++.dg/cpp2a/enum-conv1.C b/gcc/testsuite/g++.dg/cpp2a/enum-conv1.C
index d4960f334dd..4571b5e8968 100644
--- a/gcc/testsuite/g++.dg/cpp2a/enum-conv1.C
+++ b/gcc/testsuite/g++.dg/cpp2a/enum-conv1.C
@@ -110,9 +110,6 @@ enum_float (bool b)
r += b ? d : u1; // { dg-warning "conditional expression between" "" { target c++20 } }
r += b ? u1 : d; // { dg-warning "conditional expression between" "" { target c++20 } }
- // FIXME should be error
- // e1 <=> d;
-
d += e1; // { dg-warning "arithmetic between floating-point type .double. and enumeration type .E1." "" { target c++20 } }
d = e1;
diff --git a/gcc/testsuite/g++.dg/cpp2a/spaceship-err5.C b/gcc/testsuite/g++.dg/cpp2a/spaceship-err5.C
new file mode 100644
index 00000000000..3dc2a0f2365
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp2a/spaceship-err5.C
@@ -0,0 +1,23 @@
+// { dg-do compile { target c++20 } }
+// Test [depr.arith.conv.enum] for <=>.
+
+#include <compare>
+
+enum E1 { e } e1;
+enum E2 { f } e2;
+static double d;
+
+void
+g ()
+{
+ void(e1 <=> e);
+ e1 <=> d; // { dg-error "invalid operands of types .E1. and .double." }
+ d <=> e1; // { dg-error "invalid operands of types .double. and .E1." }
+ e <=> d; // { dg-error "invalid operands of types .E1. and .double." }
+ d <=> e; // { dg-error "invalid operands of types .double. and .E1." }
+
+ e <=> f; // { dg-error "invalid operands of types .E1. and .E2." }
+ f <=> e; // { dg-error "invalid operands of types .E2. and .E1." }
+ e1 <=> e2; // { dg-error "invalid operands of types .E1. and .E2." }
+ e2 <=> e1; // { dg-error "invalid operands of types .E2. and .E1." }
+}
base-commit: 4166ebedf8b8a302b86132fdf846fac204c83368
--
2.28.0
^ permalink raw reply [flat|nested] 4+ messages in thread
* Re: [PATCH] c++: Deprecate arithmetic convs on different enums [PR97573]
2020-10-29 2:46 ` Marek Polacek
@ 2020-10-29 15:19 ` Jason Merrill
0 siblings, 0 replies; 4+ messages in thread
From: Jason Merrill @ 2020-10-29 15:19 UTC (permalink / raw)
To: Marek Polacek; +Cc: GCC Patches
On 10/28/20 10:46 PM, Marek Polacek wrote:
> On Wed, Oct 28, 2020 at 02:43:30PM -0400, Jason Merrill wrote:
>> On 10/28/20 2:01 PM, Marek Polacek wrote:
>>> I noticed that C++20 P1120R0 deprecated certain arithmetic conversions
>>> as outlined in [depr.arith.conv.enum], but we don't warn about them. In
>>> particular, "If one operand is of enumeration type and the other operand
>>> is of a different enumeration type or a floating-point type, this
>>> behavior is deprecated." These will likely become ill-formed in C++23,
>>> so we should warn by default in C++20. To this effect, this patch adds
>>> two new warnings (like clang++): -Wdeprecated-enum-enum-conversion and
>>> -Wdeprecated-enum-float-conversion. They are enabled by default in
>>> C++20. In older dialects, to enable these warnings you can now use
>>> -Wenum-conversion which I made available in C++ too. Note that unlike
>>> C, in C++ it is not enabled by -Wextra, because that breaks bootstrap.
>>>
>>> We already warn about comparisons of two different enumeration types via
>>> -Wenum-compare, the rest is handled in this patch: we're performing the
>>> usual arithmetic conversions in these contexts:
>>> - an arithmetic operation,
>>> - a bitwise operation,
>>> - a comparison,
>>> - a conditional operator,
>>> - a compound assign operator.
>>>
>>> Using the spaceship operator as enum <=> real_type is ill-formed but we
>>> don't reject it yet.
>>
>> Hmm, oops. Will you fix that as well? It should be simple to fix in the
>> SPACESHIP_EXPR block that starts just at the end of this patch.
>
> Sure.
>
> Bootstrapped/regtested on x86_64-pc-linux-gnu, ok for trunk?
OK, thanks.
> From 8ae2e45f2dd35510aed3be1ab249b8612e33f00d Mon Sep 17 00:00:00 2001
> From: Marek Polacek <polacek@redhat.com>
> Date: Wed, 28 Oct 2020 19:02:29 -0400
> Subject: [PATCH] c++: Reject float <=> enum.
>
> As [depr.arith.conv.enum] says, these are ill-formed.
>
> gcc/cp/ChangeLog:
>
> * typeck.c (do_warn_enum_conversions): Don't warn for SPACESHIP_EXPR.
> (cp_build_binary_op): Reject float <=> enum or enum <=> float. Use
> CP_INTEGRAL_TYPE_P instead of INTEGRAL_OR_ENUMERATION_TYPE_P.
>
> gcc/testsuite/ChangeLog:
>
> * g++.dg/cpp2a/enum-conv1.C: Remove unused code.
> * g++.dg/cpp2a/spaceship-err5.C: New test.
> ---
> gcc/cp/typeck.c | 13 ++++++++++--
> gcc/testsuite/g++.dg/cpp2a/enum-conv1.C | 3 ---
> gcc/testsuite/g++.dg/cpp2a/spaceship-err5.C | 23 +++++++++++++++++++++
> 3 files changed, 34 insertions(+), 5 deletions(-)
> create mode 100644 gcc/testsuite/g++.dg/cpp2a/spaceship-err5.C
>
> diff --git a/gcc/cp/typeck.c b/gcc/cp/typeck.c
> index 7305310ecbe..d3b701610cf 100644
> --- a/gcc/cp/typeck.c
> +++ b/gcc/cp/typeck.c
> @@ -4512,6 +4512,9 @@ do_warn_enum_conversions (location_t loc, enum tree_code code, tree type0,
> "with enumeration type %qT is deprecated",
> type0, type1);
> return;
> + case SPACESHIP_EXPR:
> + /* This is invalid, don't warn. */
> + return;
> default:
> if (enum_first_p)
> warning_at (loc, opt, "arithmetic between enumeration type %qT "
> @@ -5584,6 +5587,12 @@ cp_build_binary_op (const op_location_t &location,
> arithmetic conversions are applied to the operands." So we don't do
> arithmetic conversions if the operands both have enumeral type. */
> result_type = NULL_TREE;
> + else if ((orig_code0 == ENUMERAL_TYPE && orig_code1 == REAL_TYPE)
> + || (orig_code0 == REAL_TYPE && orig_code1 == ENUMERAL_TYPE))
> + /* [depr.arith.conv.enum]: Three-way comparisons between such operands
> + [where one is of enumeration type and the other is of a different
> + enumeration type or a floating-point type] are ill-formed. */
> + result_type = NULL_TREE;
>
> if (result_type)
> {
> @@ -5598,12 +5607,12 @@ cp_build_binary_op (const op_location_t &location,
> type to a floating point type, the program is ill-formed. */
> bool ok = true;
> if (TREE_CODE (result_type) == REAL_TYPE
> - && INTEGRAL_OR_ENUMERATION_TYPE_P (TREE_TYPE (orig_op0)))
> + && CP_INTEGRAL_TYPE_P (orig_type0))
> /* OK */;
> else if (!check_narrowing (result_type, orig_op0, complain))
> ok = false;
> if (TREE_CODE (result_type) == REAL_TYPE
> - && INTEGRAL_OR_ENUMERATION_TYPE_P (TREE_TYPE (orig_op1)))
> + && CP_INTEGRAL_TYPE_P (orig_type1))
> /* OK */;
> else if (!check_narrowing (result_type, orig_op1, complain))
> ok = false;
> diff --git a/gcc/testsuite/g++.dg/cpp2a/enum-conv1.C b/gcc/testsuite/g++.dg/cpp2a/enum-conv1.C
> index d4960f334dd..4571b5e8968 100644
> --- a/gcc/testsuite/g++.dg/cpp2a/enum-conv1.C
> +++ b/gcc/testsuite/g++.dg/cpp2a/enum-conv1.C
> @@ -110,9 +110,6 @@ enum_float (bool b)
> r += b ? d : u1; // { dg-warning "conditional expression between" "" { target c++20 } }
> r += b ? u1 : d; // { dg-warning "conditional expression between" "" { target c++20 } }
>
> - // FIXME should be error
> - // e1 <=> d;
> -
> d += e1; // { dg-warning "arithmetic between floating-point type .double. and enumeration type .E1." "" { target c++20 } }
> d = e1;
>
> diff --git a/gcc/testsuite/g++.dg/cpp2a/spaceship-err5.C b/gcc/testsuite/g++.dg/cpp2a/spaceship-err5.C
> new file mode 100644
> index 00000000000..3dc2a0f2365
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/cpp2a/spaceship-err5.C
> @@ -0,0 +1,23 @@
> +// { dg-do compile { target c++20 } }
> +// Test [depr.arith.conv.enum] for <=>.
> +
> +#include <compare>
> +
> +enum E1 { e } e1;
> +enum E2 { f } e2;
> +static double d;
> +
> +void
> +g ()
> +{
> + void(e1 <=> e);
> + e1 <=> d; // { dg-error "invalid operands of types .E1. and .double." }
> + d <=> e1; // { dg-error "invalid operands of types .double. and .E1." }
> + e <=> d; // { dg-error "invalid operands of types .E1. and .double." }
> + d <=> e; // { dg-error "invalid operands of types .double. and .E1." }
> +
> + e <=> f; // { dg-error "invalid operands of types .E1. and .E2." }
> + f <=> e; // { dg-error "invalid operands of types .E2. and .E1." }
> + e1 <=> e2; // { dg-error "invalid operands of types .E1. and .E2." }
> + e2 <=> e1; // { dg-error "invalid operands of types .E2. and .E1." }
> +}
>
> base-commit: 4166ebedf8b8a302b86132fdf846fac204c83368
>
^ permalink raw reply [flat|nested] 4+ messages in thread
end of thread, other threads:[~2020-10-29 15:19 UTC | newest]
Thread overview: 4+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2020-10-28 18:01 [PATCH] c++: Deprecate arithmetic convs on different enums [PR97573] Marek Polacek
2020-10-28 18:43 ` Jason Merrill
2020-10-29 2:46 ` Marek Polacek
2020-10-29 15:19 ` Jason Merrill
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).