diff mbox series

C++ PATCH for c++/89836 - bool constant expression and explicit conversions

Message ID 20190327190837.GN26967@redhat.com
State New
Headers show
Series C++ PATCH for c++/89836 - bool constant expression and explicit conversions | expand

Commit Message

Marek Polacek March 27, 2019, 7:08 p.m. UTC
I noticed that this test doesn't compile because build_converted_constant_expr
failed to consider explicit conversion functions.  That's wrong: while Core
Issue 1981 specifies that explicit conversion functions are not considered for
contextual conversions, they are considered in contextual conversions to bool,
as defined in Core 2039.

Fixed by adding a new wrapper that uses LOOKUP_NORMAL instead of
LOOKUP_IMPLICIT.

Besides [dcl.fct.spec], these: [except.spec], [dcl.dcl]/6 (static_assert),
[stmt.if] (constexpr if) all talk about "a contextually converted constant
expression of type bool", so it would seem to make sense to use
build_converted_constant_bool_expr for them also.  E.g. use that instead of
perform_implicit_conversion_flags in build_noexcept_spec.  But that doesn't
work for this test:

int e();
int fn() noexcept(e);

because build_converted_constant_expr would issue a conversion error.  We're
converting "e" to "bool".  We have a ck_lvalue conversion from "int f()" to
"int (*f)()" and then a ck_std conversion from "int (*f)()" to bool.  Those
should work fine but we issue an error for the ck_std conversion.

Also build_converted_constant_expr doesn't have the processing_template_decl
handling that perform_implicit_conversion_flags does, which (I belive) lead me
to changing check_narrowing to use maybe_constant_value instead of
fold_non_dependent_expr.

Anyway, this patch should be safe.

Bootstrapped/regtested on x86_64-linux, ok for trunk?

2019-03-27  Marek Polacek  <polacek@redhat.com>

	PR c++/89836 - bool constant expression and explicit conversions.
	* call.c (build_converted_constant_expr_internal): New function,
	renamed from...
	(build_converted_constant_expr): ...this.  New.
	(build_converted_constant_bool_expr): New.
	* cp-tree.h (build_converted_constant_bool_expr): Declare.
	* decl.c (build_explicit_specifier): Call
	build_converted_constant_bool_expr.

	* g++.dg/cpp2a/explicit15.C: New test.

Comments

Jason Merrill March 28, 2019, 6:02 p.m. UTC | #1
On 3/27/19 3:08 PM, Marek Polacek wrote:
> I noticed that this test doesn't compile because build_converted_constant_expr
> failed to consider explicit conversion functions.  That's wrong: while Core
> Issue 1981 specifies that explicit conversion functions are not considered for
> contextual conversions, they are considered in contextual conversions to bool,
> as defined in Core 2039.
> 
> Fixed by adding a new wrapper that uses LOOKUP_NORMAL instead of
> LOOKUP_IMPLICIT.
> 
> Besides [dcl.fct.spec], these: [except.spec], [dcl.dcl]/6 (static_assert),
> [stmt.if] (constexpr if) all talk about "a contextually converted constant
> expression of type bool", so it would seem to make sense to use
> build_converted_constant_bool_expr for them also.  E.g. use that instead of
> perform_implicit_conversion_flags in build_noexcept_spec.  But that doesn't
> work for this test:
> 
> int e();
> int fn() noexcept(e);
> 
> because build_converted_constant_expr would issue a conversion error.  We're
> converting "e" to "bool".  We have a ck_lvalue conversion from "int f()" to
> "int (*f)()" and then a ck_std conversion from "int (*f)()" to bool.  Those
> should work fine but we issue an error for the ck_std conversion.

Converting a pointer to bool is a "boolean conversion", which is not 
allowed under the rules for a converted constant expression 
([expr.const]p7).  So we should reject that testcase.

> Also build_converted_constant_expr doesn't have the processing_template_decl
> handling that perform_implicit_conversion_flags does, which (I believe) lead me
> to changing check_narrowing to use maybe_constant_value instead of
> fold_non_dependent_expr.

As I was saying elsewhere, I think that change was probably a mistake, 
and that we should have fixed that bug by changing the caller instead.

> Anyway, this patch should be safe.
> 
> Bootstrapped/regtested on x86_64-linux, ok for trunk?

OK.

Jason
Marek Polacek March 28, 2019, 6:48 p.m. UTC | #2
On Thu, Mar 28, 2019 at 02:02:47PM -0400, Jason Merrill wrote:
> On 3/27/19 3:08 PM, Marek Polacek wrote:
> > I noticed that this test doesn't compile because build_converted_constant_expr
> > failed to consider explicit conversion functions.  That's wrong: while Core
> > Issue 1981 specifies that explicit conversion functions are not considered for
> > contextual conversions, they are considered in contextual conversions to bool,
> > as defined in Core 2039.
> > 
> > Fixed by adding a new wrapper that uses LOOKUP_NORMAL instead of
> > LOOKUP_IMPLICIT.
> > 
> > Besides [dcl.fct.spec], these: [except.spec], [dcl.dcl]/6 (static_assert),
> > [stmt.if] (constexpr if) all talk about "a contextually converted constant
> > expression of type bool", so it would seem to make sense to use
> > build_converted_constant_bool_expr for them also.  E.g. use that instead of
> > perform_implicit_conversion_flags in build_noexcept_spec.  But that doesn't
> > work for this test:
> > 
> > int e();
> > int fn() noexcept(e);
> > 
> > because build_converted_constant_expr would issue a conversion error.  We're
> > converting "e" to "bool".  We have a ck_lvalue conversion from "int f()" to
> > "int (*f)()" and then a ck_std conversion from "int (*f)()" to bool.  Those
> > should work fine but we issue an error for the ck_std conversion.
> 
> Converting a pointer to bool is a "boolean conversion", which is not allowed
> under the rules for a converted constant expression ([expr.const]p7).  So we
> should reject that testcase.

Oh, right (though clang also accepts it).  I'll see if there's anything else
that breaks if I use build_converted_constant_expr instead of
perform_implicit_conversion.

> > Also build_converted_constant_expr doesn't have the processing_template_decl
> > handling that perform_implicit_conversion_flags does, which (I believe) lead me
> > to changing check_narrowing to use maybe_constant_value instead of
> > fold_non_dependent_expr.
> 
> As I was saying elsewhere, I think that change was probably a mistake, and
> that we should have fixed that bug by changing the caller instead.

I need to address that.  Unfortunately adding a sentinel in the caller broke
so much stuff :(.

> > Anyway, this patch should be safe.
> > 
> > Bootstrapped/regtested on x86_64-linux, ok for trunk?
> 
> OK.

Thanks, committed.

Marek
diff mbox series

Patch

diff --git gcc/cp/call.c gcc/cp/call.c
index 9efca735b16..bc5179416a5 100644
--- gcc/cp/call.c
+++ gcc/cp/call.c
@@ -4175,18 +4175,11 @@  build_user_type_conversion (tree totype, tree expr, int flags,
   return ret;
 }
 
-/* Subroutine of convert_nontype_argument.
-
-   EXPR is an expression used in a context that requires a converted
-   constant-expression, such as a template non-type parameter.  Do any
-   necessary conversions (that are permitted for converted
-   constant-expressions) to convert it to the desired type.
-
-   If conversion is successful, returns the converted expression;
-   otherwise, returns error_mark_node.  */
+/* Worker for build_converted_constant_expr.  */
 
-tree
-build_converted_constant_expr (tree type, tree expr, tsubst_flags_t complain)
+static tree
+build_converted_constant_expr_internal (tree type, tree expr,
+					int flags, tsubst_flags_t complain)
 {
   conversion *conv;
   void *p;
@@ -4200,8 +4193,7 @@  build_converted_constant_expr (tree type, tree expr, tsubst_flags_t complain)
   p = conversion_obstack_alloc (0);
 
   conv = implicit_conversion (type, TREE_TYPE (expr), expr,
-			      /*c_cast_p=*/false,
-			      LOOKUP_IMPLICIT, complain);
+			      /*c_cast_p=*/false, flags, complain);
 
   /* A converted constant expression of type T is an expression, implicitly
      converted to type T, where the converted expression is a constant
@@ -4304,6 +4296,38 @@  build_converted_constant_expr (tree type, tree expr, tsubst_flags_t complain)
   return expr;
 }
 
+/* Subroutine of convert_nontype_argument.
+
+   EXPR is an expression used in a context that requires a converted
+   constant-expression, such as a template non-type parameter.  Do any
+   necessary conversions (that are permitted for converted
+   constant-expressions) to convert it to the desired type.
+
+   This function doesn't consider explicit conversion functions.  If
+   you mean to use "a contextually converted constant expression of type
+   bool", use build_converted_constant_bool_expr.
+
+   If conversion is successful, returns the converted expression;
+   otherwise, returns error_mark_node.  */
+
+tree
+build_converted_constant_expr (tree type, tree expr, tsubst_flags_t complain)
+{
+  return build_converted_constant_expr_internal (type, expr, LOOKUP_IMPLICIT,
+						 complain);
+}
+
+/* Used to create "a contextually converted constant expression of type
+   bool".  This differs from build_converted_constant_expr in that it
+   also considers explicit conversion functions.  */
+
+tree
+build_converted_constant_bool_expr (tree expr, tsubst_flags_t complain)
+{
+  return build_converted_constant_expr_internal (boolean_type_node, expr,
+						 LOOKUP_NORMAL, complain);
+}
+
 /* Do any initial processing on the arguments to a function call.  */
 
 static vec<tree, va_gc> *
diff --git gcc/cp/cp-tree.h gcc/cp/cp-tree.h
index 3fe91ad2160..fd612b0dbb1 100644
--- gcc/cp/cp-tree.h
+++ gcc/cp/cp-tree.h
@@ -6233,6 +6233,7 @@  extern int remaining_arguments			(tree);
 extern tree perform_implicit_conversion		(tree, tree, tsubst_flags_t);
 extern tree perform_implicit_conversion_flags	(tree, tree, tsubst_flags_t, int);
 extern tree build_converted_constant_expr	(tree, tree, tsubst_flags_t);
+extern tree build_converted_constant_bool_expr	(tree, tsubst_flags_t);
 extern tree perform_direct_initialization_if_possible (tree, tree, bool,
                                                        tsubst_flags_t);
 extern tree in_charge_arg_for_name		(tree);
diff --git gcc/cp/decl.c gcc/cp/decl.c
index c8435e29491..c46a39665bd 100644
--- gcc/cp/decl.c
+++ gcc/cp/decl.c
@@ -16700,7 +16700,7 @@  build_explicit_specifier (tree expr, tsubst_flags_t complain)
   expr = instantiate_non_dependent_expr_sfinae (expr, complain);
   /* Don't let convert_like_real create more template codes.  */
   processing_template_decl_sentinel s;
-  expr = build_converted_constant_expr (boolean_type_node, expr, complain);
+  expr = build_converted_constant_bool_expr (expr, complain);
   expr = cxx_constant_value (expr);
   return expr;
 }
diff --git gcc/testsuite/g++.dg/cpp2a/explicit15.C gcc/testsuite/g++.dg/cpp2a/explicit15.C
new file mode 100644
index 00000000000..e0058f6dc87
--- /dev/null
+++ gcc/testsuite/g++.dg/cpp2a/explicit15.C
@@ -0,0 +1,10 @@ 
+// PR c++/89836
+// { dg-do compile { target c++2a } }
+
+struct W { 
+  constexpr explicit operator bool() { return true; };
+};
+
+struct U {
+  explicit(W()) U(int);
+};