From a8de832470f78a40a0e2c8de866a471bf74bf0ab Mon Sep 17 00:00:00 2001 From: Jason Merrill Date: Mon, 30 Aug 2021 09:44:28 -0400 Subject: [PATCH] c++: fold function template args sooner [PR101460] As discussed in the PR, we were giving a lot of unnecessary errors for this testcase because we didn't try to do constant evaluation until convert_nontype_argument, which happens for each of the candidates. But when looking at a template-id as the function operand of a call, we can try to fold arguments before we get into overload resolution. PR c++/101460 gcc/cp/ChangeLog: * cp-tree.h (cxx_constant_value_sfinae): Declare. * constexpr.c (cxx_constant_value_sfinae): New. * pt.c (fold_targs_r, maybe_fold_fn_template_args): New. (tsubst_copy_and_build) [CALL_EXPR]: Call maybe_fold_fn_template_args. gcc/testsuite/ChangeLog: * g++.dg/template/explicit-args6.C: New test. --- gcc/cp/constexpr.c | 12 ++++++ gcc/cp/cp-tree.h | 1 + gcc/cp/pt.c | 60 ++++++++++++++++++++++++++ gcc/testsuite/g++.dg/template/explicit-args6.C | 34 +++++++++++++++ 4 files changed, 107 insertions(+) create mode 100644 gcc/testsuite/g++.dg/template/explicit-args6.C diff --git a/gcc/cp/constexpr.c b/gcc/cp/constexpr.c index b9c0062..9606719 100644 --- a/gcc/cp/constexpr.c +++ b/gcc/cp/constexpr.c @@ -7458,6 +7458,18 @@ cxx_constant_value (tree t, tree decl) return cxx_eval_outermost_constant_expr (t, false, true, true, false, decl); } +/* As above, but respect SFINAE. */ + +tree +cxx_constant_value_sfinae (tree t, tsubst_flags_t complain) +{ + bool sfinae = !(complain & tf_error); + tree r = cxx_eval_outermost_constant_expr (t, sfinae, true, true); + if (sfinae && !TREE_CONSTANT (r)) + r = error_mark_node; + return r; +} + /* Like cxx_constant_value, but used for evaluation of constexpr destructors of constexpr variables. The actual initializer of DECL is not modified. */ diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h index 75ee887..6a17937 100644 --- a/gcc/cp/cp-tree.h +++ b/gcc/cp/cp-tree.h @@ -8266,6 +8266,7 @@ extern bool require_constant_expression (tree); extern bool require_rvalue_constant_expression (tree); extern bool require_potential_rvalue_constant_expression (tree); extern tree cxx_constant_value (tree, tree = NULL_TREE); +extern tree cxx_constant_value_sfinae (tree, tsubst_flags_t); extern void cxx_constant_dtor (tree, tree); extern tree cxx_constant_init (tree, tree = NULL_TREE); extern tree maybe_constant_value (tree, tree = NULL_TREE, bool = false); diff --git a/gcc/cp/pt.c b/gcc/cp/pt.c index 020a4bf..d7d0dce 100644 --- a/gcc/cp/pt.c +++ b/gcc/cp/pt.c @@ -19465,6 +19465,63 @@ out: return r; } +/* Subroutine of maybe_fold_fn_template_args. */ + +static bool +fold_targs_r (tree targs, tsubst_flags_t complain) +{ + int len = TREE_VEC_LENGTH (targs); + for (int i = 0; i < len; ++i) + { + tree &elt = TREE_VEC_ELT (targs, i); + if (!elt || TYPE_P (elt) + || TREE_CODE (elt) == TEMPLATE_DECL) + continue; + if (TREE_CODE (elt) == NONTYPE_ARGUMENT_PACK) + { + if (!fold_targs_r (ARGUMENT_PACK_ARGS (elt), complain)) + return false; + } + else if (/* We can only safely preevaluate scalar prvalues. */ + SCALAR_TYPE_P (TREE_TYPE (elt)) + && !glvalue_p (elt) + && !TREE_CONSTANT (elt)) + { + elt = cxx_constant_value_sfinae (elt, complain); + if (elt == error_mark_node) + return false; + } + } + + return true; +} + +/* Try to do constant evaluation of any explicit template arguments in FN + before overload resolution, to get any errors only once. Return true iff + we didn't have any problems folding. */ + +static bool +maybe_fold_fn_template_args (tree fn, tsubst_flags_t complain) +{ + if (processing_template_decl || fn == NULL_TREE) + return true; + if (fn == error_mark_node) + return false; + if (TREE_CODE (fn) == OFFSET_REF + || TREE_CODE (fn) == COMPONENT_REF) + fn = TREE_OPERAND (fn, 1); + if (BASELINK_P (fn)) + fn = BASELINK_FUNCTIONS (fn); + if (TREE_CODE (fn) != TEMPLATE_ID_EXPR) + return true; + tree targs = TREE_OPERAND (fn, 1); + if (targs == NULL_TREE) + return true; + if (targs == error_mark_node) + return false; + return fold_targs_r (targs, complain); +} + /* Like tsubst but deals with expressions and performs semantic analysis. FUNCTION_P is true if T is the "F" in "F (ARGS)" or "F (ARGS)". */ @@ -20343,6 +20400,9 @@ tsubst_copy_and_build (tree t, && !mark_used (function, complain) && !(complain & tf_error)) RETURN (error_mark_node); + if (!maybe_fold_fn_template_args (function, complain)) + return error_mark_node; + /* Put back tf_decltype for the actual call. */ complain |= decltype_flag; diff --git a/gcc/testsuite/g++.dg/template/explicit-args6.C b/gcc/testsuite/g++.dg/template/explicit-args6.C new file mode 100644 index 0000000..d853564 --- /dev/null +++ b/gcc/testsuite/g++.dg/template/explicit-args6.C @@ -0,0 +1,34 @@ +// PR c++/101460 +// { dg-do compile { target c++11 } } + +template struct enable_if { }; +template<> struct enable_if { using type = void; }; + +template +using enable_if_t = typename enable_if::type; + +struct tuple { }; +struct pair { }; + +template enable_if_t get(tuple&) { } // { dg-bogus "candidate" } +template enable_if_t get(const tuple&) { } // { dg-bogus "candidate" } +template enable_if_t get(pair&) { } // { dg-bogus "candidate" } +template enable_if_t get(const pair&) { } // { dg-bogus "candidate" } + +template +constexpr unsigned +frob() +{ + static_assert(N == 1, "user-friendly diagnostic"); // { dg-error "user-friendly" } + // narrowing check, reject negative values + return unsigned{N}; // { dg-prune-output "narrowing" } +} // { dg-prune-output "flows off the end" } +// { dg-prune-output "not a return-statement" } + +template void get_n(tuple& t) { get()>(t); } // { dg-error "" } + +int main() +{ + tuple t; + get_n<-1>(t); +} -- 2.7.4