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  <pola...@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.

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);
+};

Reply via email to