diff --git a/gcc/cp/constraint.cc b/gcc/cp/constraint.cc index fcb249a642f..3332e6092a9 100644 --- a/gcc/cp/constraint.cc +++ b/gcc/cp/constraint.cc @@ -1989,39 +1989,19 @@ type_deducible_p (tree expr, tree type, tree placeholder, tree args, references are preserved in the result. */ expr = force_paren_expr_uneval (expr); - /* Replace the constraints with the instantiated constraints. This - substitutes args into any template parameters in the trailing - result type. */ - tree saved_constr = PLACEHOLDER_TYPE_CONSTRAINTS (placeholder); - tree subst_constr - = tsubst_constraint (saved_constr, - args, - info.complain | tf_partial, - info.in_decl); + /* When args is NULL, we're evaluating a non-templated requires expression, + but even those are parsed under processing_template_decl == 1, and so the + placeholder 'auto' inside this return-type-requirement has level 2. In + order to have all parms and arguments match up for satisfaction, we need + to pass an empty level of OUTER_TARGS in this case. */ + if (!args) + args = make_tree_vec (0); - if (subst_constr == error_mark_node) - return false; + tree deduced_type = do_auto_deduction (type, expr, placeholder, + info.complain, adc_requirement, + /*outer_targs=*/args); - PLACEHOLDER_TYPE_CONSTRAINTS (placeholder) = subst_constr; - - /* Temporarily unlink the canonical type. */ - tree saved_type = TYPE_CANONICAL (placeholder); - TYPE_CANONICAL (placeholder) = NULL_TREE; - - tree deduced_type - = do_auto_deduction (type, - expr, - placeholder, - info.complain, - adc_requirement); - - PLACEHOLDER_TYPE_CONSTRAINTS (placeholder) = saved_constr; - TYPE_CANONICAL (placeholder) = saved_type; - - if (deduced_type == error_mark_node) - return false; - - return true; + return deduced_type != error_mark_node; } /* True if EXPR can not be converted to TYPE. */ @@ -2286,35 +2266,10 @@ tsubst_parameter_mapping (tree map, tree args, subst_info info) return error_mark_node; tree parm = TREE_VALUE (p); tree arg = TREE_PURPOSE (p); - tree new_arg = NULL_TREE; - if (TYPE_P (arg)) - { - /* If a template parameter is declared with a placeholder, we can - get those in the argument list if decltype is applied to the - placeholder. For example: - - template - requires C - void f() { } - - The normalized argument for C will be an auto type, so we'll - need to deduce the actual argument from the corresponding - initializer (whatever argument is provided for T), and use - that result in the instantiated parameter mapping. */ - if (tree auto_node = type_uses_auto (arg)) - { - int level; - int index; - template_parm_level_and_index (parm, &level, &index); - tree init = TMPL_ARG (args, level, index); - new_arg = do_auto_deduction (arg, init, auto_node, - complain, adc_variable_type, - make_tree_vec (0)); - } - } - else if (ARGUMENT_PACK_P (arg)) + tree new_arg; + if (ARGUMENT_PACK_P (arg)) new_arg = tsubst_argument_pack (arg, args, complain, in_decl); - if (!new_arg) + else { new_arg = tsubst_template_arg (arg, args, complain, in_decl); if (TYPE_P (new_arg)) @@ -3020,6 +2975,36 @@ satisfy_associated_constraints (tree t, tree args, sat_info info) return satisfy_constraint (t, args, info); } +/* Return the normal form of the constraints on the placeholder 'auto' + type T. */ + +static tree +normalize_placeholder_type_constraints (tree t, bool diag) +{ + gcc_assert (is_auto (t)); + tree ci = PLACEHOLDER_TYPE_CONSTRAINTS_INFO (t); + if (!ci) + return NULL_TREE; + + tree constr = TREE_VALUE (ci); + /* The TREE_PURPOSE contains the set of template parameters that were in + scope for this placeholder type; use them as the initial template + parameters for normalization. */ + tree initial_parms = TREE_PURPOSE (ci); + /* The 'auto' itself is used as the first argument in its own constraints, + and its level is one greater than its template depth. So in order to + capture all used template parameters, we need to add an extra level of + template parameters to the context; a dummy level suffices. */ + initial_parms + = tree_cons (size_int (initial_parms + ? TMPL_PARMS_DEPTH (initial_parms) + 1 : 1), + make_tree_vec (0), initial_parms); + + norm_info info (diag ? tf_norm : tf_none); + info.initial_parms = initial_parms; + return normalize_constraint_expression (constr, info); +} + /* Evaluate EXPR as a constraint expression using ARGS, returning a satisfaction value. */ @@ -3029,8 +3014,6 @@ satisfy_constraint_expression (tree t, tree args, sat_info info) if (t == error_mark_node) return error_mark_node; - gcc_assert (EXPR_P (t)); - /* Get the normalized constraints. */ tree norm; if (args == NULL_TREE && concept_check_p (t)) @@ -3054,6 +3037,12 @@ satisfy_constraint_expression (tree t, tree args, sat_info info) norm_info ninfo (info.noisy () ? tf_norm : tf_none); norm = normalize_constraint_expression (t, ninfo); } + else if (is_auto (t)) + { + norm = normalize_placeholder_type_constraints (t, info.noisy ()); + if (!norm) + return boolean_true_node; + } else gcc_unreachable (); diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h index 38b31e3908f..544e99538a4 100644 --- a/gcc/cp/cp-tree.h +++ b/gcc/cp/cp-tree.h @@ -1578,11 +1578,19 @@ check_constraint_info (tree t) #define COMPOUND_REQ_NOEXCEPT_P(NODE) \ TREE_LANG_FLAG_0 (TREE_CHECK (NODE, COMPOUND_REQ)) -/* The constraints on an 'auto' placeholder type, used in an argument deduction - constraint. */ -#define PLACEHOLDER_TYPE_CONSTRAINTS(NODE) \ +/* A TREE_LIST whose TREE_VALUE is the constraints on the 'auto' placeholder + type NODE, used in an argument deduction constraint. The TREE_PURPOSE + holds the set of template parameters that were in-scope when this 'auto' + was formed. */ +#define PLACEHOLDER_TYPE_CONSTRAINTS_INFO(NODE) \ DECL_SIZE_UNIT (TYPE_NAME (NODE)) +/* The constraints on the 'auto' placeholder type NODE. */ +#define PLACEHOLDER_TYPE_CONSTRAINTS(NODE) \ + (PLACEHOLDER_TYPE_CONSTRAINTS_INFO (NODE) \ + ? TREE_VALUE (PLACEHOLDER_TYPE_CONSTRAINTS_INFO (NODE)) \ + : NULL_TREE) + /* True if NODE is a constraint. */ #define CONSTR_P(NODE) \ (TREE_CODE (NODE) == ATOMIC_CONSTR \ @@ -8420,7 +8428,7 @@ set_implicit_rvalue_p (tree ot) inline bool is_constrained_auto (const_tree t) { - return is_auto (t) && PLACEHOLDER_TYPE_CONSTRAINTS (t); + return is_auto (t) && PLACEHOLDER_TYPE_CONSTRAINTS_INFO (t); } /* RAII class to push/pop class scope T; if T is not a class, do nothing. */ diff --git a/gcc/cp/pt.c b/gcc/cp/pt.c index f324f6a1e1b..8d65a6e5bd2 100644 --- a/gcc/cp/pt.c +++ b/gcc/cp/pt.c @@ -15710,15 +15710,15 @@ tsubst (tree t, tree args, tsubst_flags_t complain, tree in_decl) ? tf_ignore_bad_quals : 0)); } else if (TREE_CODE (t) == TEMPLATE_TYPE_PARM - && PLACEHOLDER_TYPE_CONSTRAINTS (t) + && PLACEHOLDER_TYPE_CONSTRAINTS_INFO (t) && (r = (TEMPLATE_PARM_DESCENDANTS (TEMPLATE_TYPE_PARM_INDEX (t)))) && (r = TREE_TYPE (r)) - && !PLACEHOLDER_TYPE_CONSTRAINTS (r)) + && !PLACEHOLDER_TYPE_CONSTRAINTS_INFO (r)) /* Break infinite recursion when substituting the constraints of a constrained placeholder. */; else if (TREE_CODE (t) == TEMPLATE_TYPE_PARM - && !PLACEHOLDER_TYPE_CONSTRAINTS (t) + && !PLACEHOLDER_TYPE_CONSTRAINTS_INFO (t) && !CLASS_PLACEHOLDER_TEMPLATE (t) && (arg = TEMPLATE_TYPE_PARM_INDEX (t), r = TEMPLATE_PARM_DESCENDANTS (arg)) @@ -15741,8 +15741,8 @@ tsubst (tree t, tree args, tsubst_flags_t complain, tree in_decl) { /* Propagate constraints on placeholders since they are only instantiated during satisfaction. */ - if (tree constr = PLACEHOLDER_TYPE_CONSTRAINTS (t)) - PLACEHOLDER_TYPE_CONSTRAINTS (r) = constr; + if (tree ci = PLACEHOLDER_TYPE_CONSTRAINTS_INFO (t)) + PLACEHOLDER_TYPE_CONSTRAINTS_INFO (r) = ci; else if (tree pl = CLASS_PLACEHOLDER_TEMPLATE (t)) { pl = tsubst_copy (pl, args, complain, in_decl); @@ -28183,7 +28183,8 @@ make_constrained_placeholder_type (tree type, tree con, tree args) expr = build_concept_check (expr, type, args, tf_warning_or_error); --processing_template_decl; - PLACEHOLDER_TYPE_CONSTRAINTS (type) = expr; + PLACEHOLDER_TYPE_CONSTRAINTS_INFO (type) + = build_tree_list (current_template_parms, expr); /* Our canonical type depends on the constraint. */ TYPE_CANONICAL (type) = canonical_type_parameter (type); @@ -29423,9 +29424,11 @@ do_class_deduction (tree ptype, tree tmpl, tree init, from INIT. AUTO_NODE is the TEMPLATE_TYPE_PARM used for 'auto' in TYPE. The CONTEXT determines the context in which auto deduction is performed and is used to control error diagnostics. FLAGS are the LOOKUP_* flags. - OUTER_TARGS are used during template argument deduction - (context == adc_unify) to properly substitute the result, and is ignored - in other contexts. + + OUTER_TARGS is used during template argument deduction (context == adc_unify) + to properly substitute the result. It's also used in the adc_unify and + adc_requirement contexts to communicate the the necessary template arguments + to satisfaction. OUTER_TARGS is ignored in other contexts. For partial-concept-ids, extra args may be appended to the list of deduced template arguments prior to determining constraint satisfaction. */ @@ -29586,30 +29589,21 @@ do_auto_deduction (tree type, tree init, tree auto_node, } /* Check any placeholder constraints against the deduced type. */ - if (flag_concepts && !processing_template_decl) - if (tree check = NON_ERROR (PLACEHOLDER_TYPE_CONSTRAINTS (auto_node))) + if (flag_concepts) + if (NON_ERROR (PLACEHOLDER_TYPE_CONSTRAINTS (auto_node))) { - /* Use the deduced type to check the associated constraints. If we - have a partial-concept-id, rebuild the argument list so that - we check using the extra arguments. */ - check = unpack_concept_check (check); - gcc_assert (TREE_CODE (check) == TEMPLATE_ID_EXPR); - tree cdecl = TREE_OPERAND (check, 0); - if (OVL_P (cdecl)) - cdecl = OVL_FIRST (cdecl); - tree cargs = TREE_OPERAND (check, 1); - if (TREE_VEC_LENGTH (cargs) > 1) - { - cargs = copy_node (cargs); - TREE_VEC_ELT (cargs, 0) = TREE_VEC_ELT (targs, 0); - } - else - cargs = targs; + if (processing_template_decl) + /* In general we can't check satisfaction until we know all + template arguments. */ + return type; - /* Rebuild the check using the deduced arguments. */ - check = build_concept_check (cdecl, cargs, tf_none); + if ((context == adc_return_type || context == adc_variable_type) + && current_function_decl + && DECL_TEMPLATE_INFO (current_function_decl)) + outer_targs = DECL_TI_ARGS (current_function_decl); - if (!constraints_satisfied_p (check)) + tree full_targs = add_to_template_args (outer_targs, targs); + if (!constraints_satisfied_p (auto_node, full_targs)) { if (complain & tf_warning_or_error) { @@ -29634,15 +29628,16 @@ do_auto_deduction (tree type, tree init, tree auto_node, "placeholder constraints"); break; } - diagnose_constraints (input_location, check, targs); + diagnose_constraints (input_location, auto_node, full_targs); } return error_mark_node; } } - if (processing_template_decl && context != adc_unify) - outer_targs = current_template_args (); - targs = add_to_template_args (outer_targs, targs); + if (context == adc_unify) + targs = add_to_template_args (outer_targs, targs); + else if (processing_template_decl) + targs = add_to_template_args (current_template_args (), targs); return tsubst (type, targs, complain, NULL_TREE); } @@ -29669,10 +29664,12 @@ splice_late_return_type (tree type, tree late_return_type) TREE_VEC_ELT (auto_vec, 0) = new_auto; tree targs = add_outermost_template_args (current_template_args (), auto_vec); - /* FIXME: We should also rebuild the constraint to refer to the new - auto. */ - PLACEHOLDER_TYPE_CONSTRAINTS (new_auto) - = PLACEHOLDER_TYPE_CONSTRAINTS (auto_node); + /* Also rebuild the constraint info in terms of the new auto. */ + if (tree ci = PLACEHOLDER_TYPE_CONSTRAINTS_INFO (auto_node)) + PLACEHOLDER_TYPE_CONSTRAINTS_INFO (new_auto) + = build_tree_list (current_template_parms, + tsubst_constraint (TREE_VALUE (ci), targs, + tf_none, NULL_TREE)); TYPE_CANONICAL (new_auto) = canonical_type_parameter (new_auto); return tsubst (type, targs, tf_none, NULL_TREE); } diff --git a/gcc/testsuite/g++.dg/concepts/abbrev9.C b/gcc/testsuite/g++.dg/concepts/abbrev9.C new file mode 100644 index 00000000000..865b44c6a63 --- /dev/null +++ b/gcc/testsuite/g++.dg/concepts/abbrev9.C @@ -0,0 +1,26 @@ +// { dg-do compile { target concepts } } + +template concept same_as = __is_same(T, U); + +same_as auto f(auto, auto y) { + return y; // { dg-error "deduced return type" } +} + +template +struct A { + static auto g(auto x, auto y) -> same_as auto { + return y; // { dg-error "deduced return type" } + } +}; + +int main() { + f(0, 0); // { dg-bogus "" } + f("", 0); // { dg-bogus "" } + f(0, ""); // { dg-message "required from here" } + f("", ""); // { dg-message "required from here" } + + A::g(0, 0); // { dg-bogus "" } + A::g("", 0); // { dg-message "required from here" } + A::g(0, ""); // { dg-message "required from here" } + A::g("", ""); // { dg-bogus "" } +} diff --git a/gcc/testsuite/g++.dg/cpp2a/concepts-lambda15.C b/gcc/testsuite/g++.dg/cpp2a/concepts-lambda15.C new file mode 100644 index 00000000000..29df5d0b1ac --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp2a/concepts-lambda15.C @@ -0,0 +1,16 @@ +// PR c++/96960 +// { dg-do compile { target c++20 } } + +template concept C0 = true; + +template +concept C = requires(T t) { + { 42 } -> C0; +}; + +static_assert(C); + +C0 auto x = 42; + +int f(C0 auto x); +int y = f(42); diff --git a/gcc/testsuite/g++.dg/cpp2a/concepts-placeholder3.C b/gcc/testsuite/g++.dg/cpp2a/concepts-placeholder3.C new file mode 100644 index 00000000000..87e3c093e28 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp2a/concepts-placeholder3.C @@ -0,0 +1,19 @@ +// PR c++/96443 +// { dg-do compile { target c++20 } } + +template concept same_as = __is_same(T, U); + +auto f(auto x) -> same_as auto { return 0; }; // { dg-error "constraints" } +void g(auto x) { same_as auto y = 0; } // { dg-error "constraints" } +auto h(auto x) -> same_as auto { return 0; } // { dg-error "constraints|missing" } +template auto N> void i() {} + +int main() { + f(0); // { dg-bogus "" } + f(true); // { dg-message "required from here" } + g(0); // { dg-bogus "" } + g(true); // { dg-message "required from here" } + h(0); // { dg-message "required from here" } + i(); // { dg-bogus "" } + i(); // { dg-error "no match|constraints" } +} diff --git a/gcc/testsuite/g++.dg/cpp2a/concepts-return-req2.C b/gcc/testsuite/g++.dg/cpp2a/concepts-return-req2.C new file mode 100644 index 00000000000..77208bb7069 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp2a/concepts-return-req2.C @@ -0,0 +1,13 @@ +// Verify we check return-type-requirements by passing the entire set of +// template arguments to normalization rather than first substituting into +// the constraint. The latter approach would induce a substitution failure and +// cause the requires-expression to evaluate to false here. +// { dg-do compile { target c++20 } } + +template +concept C1 = true; + +template +concept C2 = requires { { 0 } -> C1; }; + +static_assert(C2); diff --git a/gcc/testsuite/g++.dg/cpp2a/concepts-ts1.C b/gcc/testsuite/g++.dg/cpp2a/concepts-ts1.C index 1cefe3b243f..a116cac4ea4 100644 --- a/gcc/testsuite/g++.dg/cpp2a/concepts-ts1.C +++ b/gcc/testsuite/g++.dg/cpp2a/concepts-ts1.C @@ -40,7 +40,7 @@ void driver() f3('a'); // { dg-error "" } f4(0, 0); f4(0, 'a'); // { dg-error "" } - f15(0); + f15(0); // { dg-bogus "" } f15('a'); // { dg-message "" } }