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);
-
- if (subst_constr == error_mark_node)
- return false;
-
- 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;
+ /* 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 (deduced_type == error_mark_node)
- return false;
+ tree deduced_type = do_auto_deduction (type, expr, placeholder,
+ info.complain, adc_requirement,
+ /*outer_targs=*/args);
- return true;
+ return deduced_type != error_mark_node;
}
/* True if EXPR can not be converted to TYPE. */
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<auto T>
- requires C<decltype(T)>
- 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))
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. */
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))
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 ();
#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 \
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. */
? 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))
{
/* 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);
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);
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. */
}
/* 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)
{
"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);
}
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);
}
--- /dev/null
+// { dg-do compile { target concepts } }
+
+template <class T, class U> concept same_as = __is_same(T, U);
+
+same_as<int> auto f(auto, auto y) {
+ return y; // { dg-error "deduced return type" }
+}
+
+template <class>
+struct A {
+ static auto g(auto x, auto y) -> same_as<decltype(x)> 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<void>::g(0, 0); // { dg-bogus "" }
+ A<void>::g("", 0); // { dg-message "required from here" }
+ A<void>::g(0, ""); // { dg-message "required from here" }
+ A<void>::g("", ""); // { dg-bogus "" }
+}
--- /dev/null
+// PR c++/96960
+// { dg-do compile { target c++20 } }
+
+template <class, class> concept C0 = true;
+
+template <class T>
+concept C = requires(T t) {
+ { 42 } -> C0<char [([] { return 42; }())]>;
+};
+
+static_assert(C<int>);
+
+C0<char [([] { return 42; }())]> auto x = 42;
+
+int f(C0<char [([] { return 42; }())]> auto x);
+int y = f(42);
--- /dev/null
+// PR c++/96443
+// { dg-do compile { target c++20 } }
+
+template <class T, class U> concept same_as = __is_same(T, U);
+
+auto f(auto x) -> same_as<decltype(x)> auto { return 0; }; // { dg-error "constraints" }
+void g(auto x) { same_as<decltype(x)> auto y = 0; } // { dg-error "constraints" }
+auto h(auto x) -> same_as<decltype(x.missing)> auto { return 0; } // { dg-error "constraints|missing" }
+template <class T, same_as<T> 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<int, 0>(); // { dg-bogus "" }
+ i<int, true>(); // { dg-error "no match|constraints" }
+}
--- /dev/null
+// 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 <class, class>
+concept C1 = true;
+
+template <class T>
+concept C2 = requires { { 0 } -> C1<typename T::type>; };
+
+static_assert(C2<int>);
f3('a'); // { dg-error "" }
f4(0, 0);
f4(0, 'a'); // { dg-error "" }
- f15(0);
+ f15(0); // { dg-bogus "" }
f15('a'); // { dg-message "" }
}