The point of this patch is to fix the recurring problem of trees generated by convert_like while processing a template that break when substituting. For instance, when convert_like creates a CALL_EXPR while in a template, substituting such a call breaks in finish_call_expr because we have two 'this' arguments. Another problem is that we can create &TARGET_EXPR<> and then fail when substituting because we're taking the address of an rvalue. I've analyzed some of the already fixed PRs and also some of the currently open ones:
In c++/93870 we create EnumWrapper<E>::operator E(&operator~(E)). In c++/87145 we create S::operator int (&{N}). In c++/92031 we create &TARGET_EXPR <0>. And so on. I'd like to fix it once and for all. I wanted something that fixes all the existing cases, removes the ugly check in convert_nontype_argument, and something suitable for stage4. I.e., I didn't implement any cleanups suggested in <https://gcc.gnu.org/ml/gcc-patches/2020-02/msg00832.html> regarding the pattern in e.g. build_explicit_specifier. The gist of the problem is when convert_like_real creates a call for a ck_user or wraps a TARGET_EXPR in & in a template. So in these cases use IMPLICIT_CONV_EXPR. In a template we shouldn't need to perform the actual conversion, we only need it's result type. Is that something that convert_like_real shouldn't do? perform_direct_initialization_if_possible and perform_implicit_conversion_flags can also create an IMPLICIT_CONV_EXPR. Given the change above, build_converted_constant_expr can return an IMPLICIT_CONV_EXPR so call fold_non_dependent_expr rather than maybe_constant_value to deal with that. A problem with that is that now we may instantiate something twice in a row (?). Handling all of it in build_converted_constant_expr won't be that straightforward because we sometimes call cxx_constant_value to give errors, or use manifestly_const_eval which should be honored. Bootstrapped/regtested on x86_64-linux, ok for trunk? 2020-02-29 Marek Polacek <pola...@redhat.com> PR c++/92031 - bogus taking address of rvalue error. PR c++/91465 - ICE with template codes in check_narrowing. PR c++/93870 - wrong error when converting template non-type arg. * call.c (convert_like_real) <case ck_user>: Return IMPLICIT_CONV_EXPR in a template. (convert_like_real) <case ck_ref_bind>: Likewise. * decl.c (compute_array_index_type_loc): Call fold_non_dependent_expr instead of maybe_constant_value. * pt.c (convert_nontype_argument): Don't build IMPLICIT_CONV_EXPR. Set IMPLICIT_CONV_EXPR_NONTYPE_ARG if that's what build_converted_constant_expr returned. * typeck2.c (check_narrowing): Call fold_non_dependent_expr instead of maybe_constant_value. * g++.dg/cpp0x/conv-tmpl2.C: New test. * g++.dg/cpp0x/conv-tmpl3.C: New test. * g++.dg/cpp0x/conv-tmpl4.C: New test. * g++.dg/cpp1z/conv-tmpl1.C: New test. --- gcc/cp/call.c | 12 +++++++++ gcc/cp/decl.c | 4 +-- gcc/cp/pt.c | 25 ++++--------------- gcc/cp/typeck2.c | 6 ++++- gcc/testsuite/g++.dg/cpp0x/conv-tmpl2.C | 21 ++++++++++++++++ gcc/testsuite/g++.dg/cpp0x/conv-tmpl3.C | 16 ++++++++++++ gcc/testsuite/g++.dg/cpp0x/conv-tmpl4.C | 33 +++++++++++++++++++++++++ gcc/testsuite/g++.dg/cpp1z/conv-tmpl1.C | 10 ++++++++ 8 files changed, 104 insertions(+), 23 deletions(-) create mode 100644 gcc/testsuite/g++.dg/cpp0x/conv-tmpl2.C create mode 100644 gcc/testsuite/g++.dg/cpp0x/conv-tmpl3.C create mode 100644 gcc/testsuite/g++.dg/cpp0x/conv-tmpl4.C create mode 100644 gcc/testsuite/g++.dg/cpp1z/conv-tmpl1.C diff --git a/gcc/cp/call.c b/gcc/cp/call.c index 85bbd043a1d..4cb07b61695 100644 --- a/gcc/cp/call.c +++ b/gcc/cp/call.c @@ -7383,6 +7383,12 @@ convert_like_real (conversion *convs, tree expr, tree fn, int argnum, { struct z_candidate *cand = convs->cand; + /* Creating &TARGET_EXPR<> in a template breaks when substituting, + and creating a CALL_EXPR in a template breaks in finish_call_expr + so use an IMPLICIT_CONV_EXPR for this conversion. */ + if (processing_template_decl) + return build1 (IMPLICIT_CONV_EXPR, totype, expr); + if (cand == NULL) /* We chose the surrogate function from add_conv_candidate, now we actually need to build the conversion. */ @@ -7760,6 +7766,12 @@ convert_like_real (conversion *convs, tree expr, tree fn, int argnum, expr = convert_bitfield_to_declared_type (expr); expr = fold_convert (type, expr); } + + /* Creating &TARGET_EXPR<> in a template would break when + tsubsting the expression, so use an IMPLICIT_CONV_EXPR + instead. */ + if (processing_template_decl) + return build1 (IMPLICIT_CONV_EXPR, totype, expr); expr = build_target_expr_with_type (expr, type, complain); } diff --git a/gcc/cp/decl.c b/gcc/cp/decl.c index e3f4b435a49..d04b042f880 100644 --- a/gcc/cp/decl.c +++ b/gcc/cp/decl.c @@ -10281,8 +10281,8 @@ compute_array_index_type_loc (location_t name_loc, tree name, tree size, /* Pedantically a constant expression is required here and so __builtin_is_constant_evaluated () should fold to true if it is successfully folded into a constant. */ - size = maybe_constant_value (size, NULL_TREE, - /*manifestly_const_eval=*/true); + size = fold_non_dependent_expr (size, complain, + /*manifestly_const_eval=*/true); if (!TREE_CONSTANT (size)) size = origsize; diff --git a/gcc/cp/pt.c b/gcc/cp/pt.c index 622c70b352f..daeaec5ad93 100644 --- a/gcc/cp/pt.c +++ b/gcc/cp/pt.c @@ -7068,26 +7068,6 @@ convert_nontype_argument (tree type, tree expr, tsubst_flags_t complain) else if (INTEGRAL_OR_ENUMERATION_TYPE_P (type) || cxx_dialect >= cxx17) { - /* Calling build_converted_constant_expr might create a call to - a conversion function with a value-dependent argument, which - could invoke taking the address of a temporary representing - the result of the conversion. */ - if (!same_type_ignoring_top_level_qualifiers_p (type, expr_type) - && ((COMPOUND_LITERAL_P (expr) - && CONSTRUCTOR_IS_DEPENDENT (expr) - && MAYBE_CLASS_TYPE_P (expr_type) - && TYPE_HAS_CONVERSION (expr_type)) - /* Similarly, converting e.g. an integer to a class - involves a constructor call. convert_like would - create a TARGET_EXPR, but in a template we can't - use AGGR_INIT_EXPR, and the TARGET_EXPR would lead - to a bogus error. */ - || (val_dep_p && MAYBE_CLASS_TYPE_P (type)))) - { - expr = build1 (IMPLICIT_CONV_EXPR, type, expr); - IMPLICIT_CONV_EXPR_NONTYPE_ARG (expr) = true; - return expr; - } /* C++17: A template-argument for a non-type template-parameter shall be a converted constant expression (8.20) of the type of the template-parameter. */ @@ -7096,6 +7076,11 @@ convert_nontype_argument (tree type, tree expr, tsubst_flags_t complain) /* Make sure we return NULL_TREE only if we have really issued an error, as described above. */ return (complain & tf_error) ? NULL_TREE : error_mark_node; + else if (TREE_CODE (expr) == IMPLICIT_CONV_EXPR) + { + IMPLICIT_CONV_EXPR_NONTYPE_ARG (expr) = true; + return expr; + } expr = maybe_constant_value (expr, NULL_TREE, /*manifestly_const_eval=*/true); expr = convert_from_reference (expr); diff --git a/gcc/cp/typeck2.c b/gcc/cp/typeck2.c index 48920894b3b..bff4ddbcf81 100644 --- a/gcc/cp/typeck2.c +++ b/gcc/cp/typeck2.c @@ -981,7 +981,11 @@ check_narrowing (tree type, tree init, tsubst_flags_t complain, return ok; } - init = maybe_constant_value (init); + /* Even non-dependent expressions can still have template + codes like CAST_EXPR, so use *_non_dependent_expr to cope. */ + init = fold_non_dependent_expr (init, complain); + if (init == error_mark_node) + return ok; /* If we were asked to only check constants, return early. */ if (const_only && !TREE_CONSTANT (init)) diff --git a/gcc/testsuite/g++.dg/cpp0x/conv-tmpl2.C b/gcc/testsuite/g++.dg/cpp0x/conv-tmpl2.C new file mode 100644 index 00000000000..8a505769c3c --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp0x/conv-tmpl2.C @@ -0,0 +1,21 @@ +// PR c++/92031 - bogus taking address of rvalue error. +// { dg-do compile { target c++11 } } + +struct x { const int& l; }; + +void a(const x&) {} + +template<class E> +void f() { + a(x { 0 }); +} + +void g() { + a(x { 0 }); +} + +void +test () +{ + f<int>(); +} diff --git a/gcc/testsuite/g++.dg/cpp0x/conv-tmpl3.C b/gcc/testsuite/g++.dg/cpp0x/conv-tmpl3.C new file mode 100644 index 00000000000..e2021aa13e1 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp0x/conv-tmpl3.C @@ -0,0 +1,16 @@ +// PR c++/91465 - ICE with template codes in check_narrowing. +// { dg-do compile { target c++11 } } + +enum class D { X }; +enum class S { Z }; + +D foo(S) { return D{}; } +D foo(double) { return D{}; } + +template <typename> +struct Bar { + D baz(S s) + { + return D{foo(s)}; + } +}; diff --git a/gcc/testsuite/g++.dg/cpp0x/conv-tmpl4.C b/gcc/testsuite/g++.dg/cpp0x/conv-tmpl4.C new file mode 100644 index 00000000000..966a2e1ac9e --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp0x/conv-tmpl4.C @@ -0,0 +1,33 @@ +// PR c++/93870 - wrong error when converting template non-type arg. +// { dg-do compile { target c++11 } } + +template <typename ENUM> struct EnumWrapper +{ + ENUM value; + + constexpr operator ENUM() const + { + return value; + } +}; + +enum E : int { V }; + +constexpr EnumWrapper<E> operator ~(E a) +{ + return {E(~int(a))}; +} + +template <E X> struct R +{ + static void Func(); +}; + +template <E X> struct S : R<~X> +{ +}; + +void Test() +{ + S<E::V>::Func(); +} diff --git a/gcc/testsuite/g++.dg/cpp1z/conv-tmpl1.C b/gcc/testsuite/g++.dg/cpp1z/conv-tmpl1.C new file mode 100644 index 00000000000..5b1205349d0 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp1z/conv-tmpl1.C @@ -0,0 +1,10 @@ +// PR c++/91465 - ICE with template codes in check_narrowing. +// { dg-do compile { target c++17 } } + +enum class E { Z }; + +template <typename F> +void foo(F) +{ + E{char(0)}; +} base-commit: bd55ce63657b42e32040d1e09b4cd76fe3705474 -- Marek Polacek • Red Hat, Inc. • 300 A St, Boston, MA