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.
Tested x86_64-pc-linux-gnu, applying to trunk. 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/cp-tree.h | 1 + gcc/cp/constexpr.c | 12 ++++ gcc/cp/pt.c | 60 +++++++++++++++++++ .../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/cp-tree.h b/gcc/cp/cp-tree.h index 75ee88721b4..6a179375a56 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/constexpr.c b/gcc/cp/constexpr.c index b9c006217be..9606719bc73 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/pt.c b/gcc/cp/pt.c index 020a4bf2f6d..d7d0dce691c 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<TARGS> (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 00000000000..d853564e3be --- /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<bool> struct enable_if { }; +template<> struct enable_if<true> { using type = void; }; + +template<bool B> +using enable_if_t = typename enable_if<B>::type; + +struct tuple { }; +struct pair { }; + +template<unsigned N> enable_if_t<N == 1> get(tuple&) { } // { dg-bogus "candidate" } +template<unsigned N> enable_if_t<N == 1> get(const tuple&) { } // { dg-bogus "candidate" } +template<unsigned N> enable_if_t<N == 1> get(pair&) { } // { dg-bogus "candidate" } +template<unsigned N> enable_if_t<N == 1> get(const pair&) { } // { dg-bogus "candidate" } + +template<int N> +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<int N> void get_n(tuple& t) { get<frob<N>()>(t); } // { dg-error "" } + +int main() +{ + tuple t; + get_n<-1>(t); +} base-commit: e18e56c76be35e6a799e07a01c24e0fff3eb1978 -- 2.27.0