tree in_decl;
};
+static tree satisfy_constraint (tree, tree, subst_info);
+
/* True if T is known to be some type other than bool. Note that this
is false for dependent types and errors. */
tree parms = DECL_TEMPLATE_PARMS (decl);
depth = TREE_INT_CST_LOW (TREE_PURPOSE (parms));
}
+ else if (current_template_parms)
+ {
+ /* TODO: This should probably be the only case, but because the
+ point of declaration of concepts is currently set after the
+ initializer, the template parameter lists are not available
+ when normalizing concept definitions, hence the case above. */
+ depth = TMPL_PARMS_DEPTH (current_template_parms);
+ }
+
tree parms = find_template_parameters (expr, depth);
tree map = map_arguments (parms, args);
return map;
struct norm_info : subst_info
{
- norm_info(tsubst_flags_t complain)
+ explicit norm_info (tsubst_flags_t complain)
: subst_info (tf_warning_or_error | complain, NULL_TREE),
context()
{}
return get_normalized_constraints_from_decl (decl, diag);
}
+/* Normalize an EXPR as a constraint using ARGS. */
+
+static tree
+normalize_constraint_expression (tree expr, tree args, bool diag = false)
+{
+ if (!expr || expr == error_mark_node)
+ return expr;
+ ++processing_template_decl;
+ norm_info info (diag ? tf_norm : tf_none);
+ tree norm = get_normalized_constraints (expr, args, info);
+ --processing_template_decl;
+ return norm;
+}
+
/* Normalize an EXPR as a constraint. */
static tree
else
args = NULL_TREE;
- ++processing_template_decl;
- norm_info info (diag ? tf_norm : tf_none);
- tree norm = get_normalized_constraints (expr, args, info);
- --processing_template_decl;
- return norm;
+ return normalize_constraint_expression (expr, args, diag);
}
/* 17.4.1.2p2. Two constraints are identical if they are formed
static tree
tsubst_nested_requirement (tree t, tree args, subst_info info)
{
- tree t0 = TREE_OPERAND (t, 0);
- tree expr = tsubst_expr (t0, args, info.complain, info.in_decl, false);
- if (expr == error_mark_node)
- return error_mark_node;
-
- /* Ensure that concrete results are satisfied. */
- if (!uses_template_parms (args))
- {
- /* FIXME satisfy_constraint_expression (t0, args, info) */
-
- /* [17.4.1.2] ... lvalue-to-value conversion is performed as necessary,
- and EXPR shall be a constant expression of type bool. */
- tree result = force_rvalue (expr, tf_error);
- if (result == error_mark_node)
- return error_mark_node;
-
- /* FIXME: The expression must have boolean type. */
- if (cv_unqualified (TREE_TYPE (result)) != boolean_type_node)
- return error_mark_node;
-
- /* Compute the value of the expression. */
- result = satisfaction_value (cxx_constant_value (result));
- if (result == error_mark_node || result == boolean_false_node)
- return error_mark_node;
- }
+ gcc_assert (!uses_template_parms (args));
- return finish_nested_requirement (EXPR_LOCATION (t), expr);
+ /* Ensure that we're in an evaluation context prior to satisfaction. */
+ tree norm = TREE_VALUE (TREE_TYPE (t));
+ tree result = satisfy_constraint (norm, args, info);
+ if (result != boolean_true_node)
+ return error_mark_node;
+ return result;
}
/* Substitute ARGS into the requirement T. */
tree
get_mapped_args (tree map)
{
- /* If there's no map, then there are no arguments. */
+ /* No map, no arguments. */
if (!map)
return NULL_TREE;
list[index] = TREE_PURPOSE (p);
}
- /* Build the actual argument list. */
+ /* Build the new argument list. */
tree args = make_tree_vec (lists.length ());
for (unsigned i = 0; i != lists.length (); ++i)
{
removed before returning. */
diagnosing_failed_constraint failure (t, args, info.noisy ());
- /* Instantiate the parameter mapping, so that we map directly to
- the arguments provided to the instantiation. */
+ /* Instantiate the parameter mapping. */
tree map = tsubst_parameter_mapping (ATOMIC_CONSTR_MAP (t), args, quiet);
if (map == error_mark_node)
{
/* We need to check access during satisfaction. */
deferring_access_check_sentinel acs (dk_no_deferred);
- /* Avoid early exit in tsubst and tsubst_copy from null args. */
- if (args == NULL_TREE)
- args = make_tree_vec (1);
-
return satisfy_constraint_r (t, args, info);
}
tree
finish_nested_requirement (location_t loc, tree expr)
{
- tree r = build_nt (NESTED_REQ, expr);
+ /* Save the normalized constraint and complete set of normalization
+ arguments with the requirement. We keep the complete set of arguments
+ around for re-normalization during diagnostics. */
+ tree args = current_template_parms
+ ? template_parms_to_args (current_template_parms) : NULL_TREE;
+ tree norm = normalize_constraint_expression (expr, args, false);
+ tree info = build_tree_list (args, norm);
+
+ /* Build the constraint, saving its normalization as its type. */
+ tree r = build1 (NESTED_REQ, info, expr);
SET_EXPR_LOCATION (r, loc);
return r;
}
static void
diagnose_nested_requirement (tree req, tree args)
{
- tree expr = TREE_OPERAND (req, 0);
- if (constraints_satisfied_p (expr, args))
+ /* Quietly check for satisfaction first. We can elaborate details
+ later if needed. */
+ tree norm = TREE_VALUE (TREE_TYPE (req));
+ subst_info info (tf_none, NULL_TREE);
+ tree result = satisfy_constraint (norm, args, info);
+ if (result == boolean_true_node)
return;
+
+ tree expr = TREE_OPERAND (req, 0);
location_t loc = cp_expr_location (expr);
inform (loc, "nested requirement %qE is not satisfied", expr);
/* TODO: Replay the substitution to diagnose the error? */
// subst_info noisy (tf_warning_or_error, NULL_TREE);
- // constraints_satisfied_p (expr, args, noisy);
+ // satisfy_constraint (norm, args, info);
}
static void
if (TREE_TYPE (t))
WALK_SUBTREE (TREE_TYPE (t));
break;
+
+ case PARM_DECL:
+ /* A parameter or constraint variable may also depend on a template
+ parameter without explicitly naming it. */
+ WALK_SUBTREE (TREE_TYPE (t));
+ break;
+
default:
break;
}
if (parm_packs == NULL_TREE)
return false;
else if (has_empty_arg)
- return true;
+ {
+ /* If all the actual packs are pack expansions, we can still
+ subsitute directly. */
+ for (tree p = parm_packs; p; p = TREE_CHAIN (p))
+ {
+ tree a = TREE_VALUE (p);
+ if (TREE_CODE (a) == ARGUMENT_PACK_SELECT)
+ a = ARGUMENT_PACK_SELECT_FROM_PACK (a);
+ a = ARGUMENT_PACK_ARGS (a);
+ if (TREE_VEC_LENGTH (a) == 1)
+ a = TREE_VEC_ELT (a, 0);
+ if (PACK_EXPANSION_P (a))
+ continue;
+ return true;
+ }
+ return false;
+ }
bool has_expansion_arg = false;
for (int i = 0 ; i < arg_pack_len; ++i)
gcc_assert (!TREE_PURPOSE (extra));
extra = TREE_VALUE (extra);
}
- return add_to_template_args (extra, args);
+#if 1
+ /* I think we should always be able to substitute dependent args into the
+ pattern. If that turns out to be incorrect in some cases, enable the
+ alternate code (and add complain/in_decl parms to this function). */
+ gcc_checking_assert (!uses_template_parms (extra));
+#else
+ if (!uses_template_parms (extra))
+ {
+ gcc_unreachable ();
+ extra = tsubst_template_args (extra, args, complain, in_decl);
+ args = add_outermost_template_args (args, extra);
+ }
+ else
+#endif
+ args = add_to_template_args (extra, args);
+ return args;
}
/* Substitute ARGS into T, which is an pack expansion
--- /dev/null
+// { dg-do compile { target c++2a } }
+
+template<typename T>
+concept integer = __is_same_as(T, int);
+
+template<typename T>
+concept subst = requires (T x) { requires true; };
+
+template<typename T>
+concept c1 = requires { requires integer<T> || subst<T&>; }; // { dg-message "in requirements" }
+
+static_assert(requires { requires true; });
+static_assert(requires { requires false; }); // { dg-error "static assertion failed" }
+static_assert(requires { requires integer<int>; });
+static_assert(requires { requires integer<void>; }); // { dg-error "static assertion failed" }
+static_assert(requires { requires c1<int>; });
+static_assert(requires { requires c1<bool>; });
+static_assert(requires { requires c1<void>; }); // { dg-error "static assertion failed" }
+static_assert(requires { requires subst<void&>; }); // { dg-error "cannot declare|failed" }
+
+static_assert(c1<int>);
+static_assert(c1<bool>);
+static_assert(c1<void>); // { dg-error "static assertion failed" }
+
+template<c1 T>
+void f1() { }
+
+template<typename T>
+ requires requires { requires integer<T> || subst<T&>; } // { dg-message "in requirements" }
+void f2();
+
+template<typename T>
+struct data
+{
+ template<c1 U>
+ void f1() {}
+
+ template<typename U>
+ requires requires { requires integer<U> || subst<U&>; } // { dg-message in requirements" }
+ void f2() {}
+
+ static_assert(requires { requires subst<T&>; }); // { dg-error "forming reference|failed" }
+
+ template<typename U>
+ constexpr bool test()
+ {
+ if constexpr (requires { requires subst<U&>; }) // { dg-error "forming reference" }
+ return true;
+ else
+ return false;
+ }
+};
+
+void test()
+{
+ f1<int>();
+ f1<bool>();
+ f1<void>(); // { dg-error "unsatisfied" }
+
+ f2<int>();
+ f2<bool>();
+ f2<void>(); // { dg-error "unsatisfied" }
+
+ data<char> x;
+ x.f1<int>();
+ x.f1<bool>();
+ x.f1<void>(); // { dg-error "no matching function" }
+ x.f2<int>();
+ x.f2<bool>();
+ x.f2<void>(); // { dg-error "no matching function" }
+
+ data<void> fail;
+
+ data<int> t;
+ static_assert(t.test<int>());
+ static_assert(t.test<void>()); // { dg-error "static assertion failed" }
+}