The fix for 77656 caused us to call convert_nontype_argument even for
value-dependent arguments, to perform the conversion in order to avoid
a bogus warning.

In this case, the argument is Pod{N}.  The call to build_converted_constant_expr
in convert_nontype_argument produces Pod::operator Enum(&{N}).  It doesn't crash
because we're in a template and build_address no longer crashes on CONSTRUCTORs
in a template.

Then when instantiating the function foo we substitute its argument: &{N}.  So
we're in tsubst_copy_and_build/ADDR_EXPR.  The call to
tsubst_non_call_postfix_expression turns {N} into TARGET_EXPR <D.2329, 
{.val=2}>.
Then build_x_unary_op is supposed to put the ADDR_EXPR back.  It calls
cp_build_addr_expr_strict.  But it's *strict*, so the prvalue of class type
TARGET_EXPR <D.2329, {.val=2}> isn't allowed -> error.

It's _strict since <https://gcc.gnu.org/ml/gcc-patches/2010-09/msg02144.html>,
that seem like a desirable change, and we had a warning for taking the address
of a TARGET_EXPR in build_x_unary_op even before that.

So rather than messing with _strict, let's avoid this scenario altogether.
I checked whether we have a case in the testsuite that results in convert_like
getting a value-dependent CONSTRUCTOR, but found none.

With this patch, we avoid it, and only call convert_nontype_argument after
substitution, at which point maybe_constant_value will be able to evaluate
the conversion to a constant.

This problem doesn't occur when passing Pod{N} as an argument to a function,
or using it as an array dimension; seems we avoid converting the argument if
it's value-dependent.

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

2019-03-20  Marek Polacek  <pola...@redhat.com>

        PR c++/87145 - bogus error converting class type in template arg list.
        * pt.c (convert_template_argument): Don't call convert_nontype_argument
        if it could involve calling a conversion function with a value-dependent
        constructor as its argument.

        * g++.dg/cpp0x/constexpr-conv3.C: New test.
        * g++.dg/cpp0x/constexpr-conv4.C: New test.

diff --git gcc/cp/pt.c gcc/cp/pt.c
index 0acc16d1b92..6878583d99b 100644
--- gcc/cp/pt.c
+++ gcc/cp/pt.c
@@ -8056,7 +8056,16 @@ convert_template_argument (tree parm,
        t = canonicalize_type_argument (t, complain);
 
       if (!type_dependent_expression_p (orig_arg)
-         && !uses_template_parms (t))
+         && !uses_template_parms (t)
+         /* This might trigger calling a conversion function with
+            a value-dependent argument, which could invoke taking
+            the address of a temporary representing the result of
+            the conversion.  */
+         && !(COMPOUND_LITERAL_P (orig_arg)
+              && MAYBE_CLASS_TYPE_P (TREE_TYPE (orig_arg))
+              && TYPE_HAS_CONVERSION (TREE_TYPE (orig_arg))
+              && INTEGRAL_OR_ENUMERATION_TYPE_P (t)
+              && value_dependent_expression_p (orig_arg)))
        /* We used to call digest_init here.  However, digest_init
           will report errors, which we don't want when complain
           is zero.  More importantly, digest_init will try too
@@ -8092,7 +8101,7 @@ convert_template_argument (tree parm,
              && TREE_CODE (TREE_TYPE (innertype)) == FUNCTION_TYPE
              && TREE_OPERAND_LENGTH (inner) > 0
               && reject_gcc_builtin (TREE_OPERAND (inner, 0)))
-              return error_mark_node;
+           return error_mark_node;
         }
 
       if (TREE_CODE (val) == SCOPE_REF)
diff --git gcc/testsuite/g++.dg/cpp0x/constexpr-conv3.C 
gcc/testsuite/g++.dg/cpp0x/constexpr-conv3.C
new file mode 100644
index 00000000000..3f47c58cd2a
--- /dev/null
+++ gcc/testsuite/g++.dg/cpp0x/constexpr-conv3.C
@@ -0,0 +1,25 @@
+// PR c++/87145
+// { dg-do compile { target c++11 } }
+
+template<typename T, T t> struct integral_constant {
+  static constexpr T value = t;
+};
+
+enum class Enum : unsigned {};
+
+struct Pod {
+  unsigned val;
+
+  constexpr operator Enum() const {
+    return static_cast<Enum>(val);
+  }
+};
+
+template<unsigned N>
+constexpr void foo() {
+  using Foo = integral_constant<Enum, Pod{N}>;
+}
+
+int main() {
+  foo<2>();
+}
diff --git gcc/testsuite/g++.dg/cpp0x/constexpr-conv4.C 
gcc/testsuite/g++.dg/cpp0x/constexpr-conv4.C
new file mode 100644
index 00000000000..f4e3f00a585
--- /dev/null
+++ gcc/testsuite/g++.dg/cpp0x/constexpr-conv4.C
@@ -0,0 +1,25 @@
+// PR c++/87145
+// { dg-do compile { target c++11 } }
+
+struct S {
+  int val;
+
+  constexpr operator int() const {
+    return static_cast<int>(val);
+  }
+};
+
+template<int N>
+struct F { };
+
+template<unsigned N>
+constexpr void foo() {
+  F<int{N}> f;
+  F<S{N}> f2;
+}
+
+int
+main()
+{
+  foo<2>();
+}

Reply via email to