/* Processing rules for constraints.
- Copyright (C) 2013-2022 Free Software Foundation, Inc.
+ Copyright (C) 2013-2024 Free Software Foundation, Inc.
Contributed by Andrew Sutton (andrew.n.sutton@gmail.com)
This file is part of GCC.
return rhs;
if (!rhs)
return lhs;
- return finish_constraint_and_expr (input_location, lhs, rhs);
+ /* Use UNKNOWN_LOCATION so write_template_args can tell the difference
+ between this and a && the user wrote. */
+ return finish_constraint_and_expr (UNKNOWN_LOCATION, lhs, rhs);
}
/* Extract the template-id from a concept check. For standard and variable
/* Remember the candidate if we can deduce a substitution. */
++processing_template_decl;
tree parms = TREE_VALUE (DECL_TEMPLATE_PARMS (tmpl));
- if (tree subst = coerce_template_parms (parms, args, tmpl))
+ if (tree subst = coerce_template_parms (parms, args, tmpl, tf_none))
{
if (subst == error_mark_node)
++nerrs;
tree args = TREE_OPERAND (id, 1);
tree parms = INNERMOST_TEMPLATE_PARMS (DECL_TEMPLATE_PARMS (tmpl));
++processing_template_decl;
- tree result = coerce_template_parms (parms, args, tmpl);
+ tree result = coerce_template_parms (parms, args, tmpl, tf_none);
--processing_template_decl;
if (result == error_mark_node)
return error_mark_node;
return build2 (c, ci, t0, t1);
}
+/* Data types and hash functions for caching the normal form of a concept-id.
+ This essentially memoizes calls to normalize_concept_check. */
+
+struct GTY((for_user)) norm_entry
+{
+ /* The CONCEPT_DECL of the concept-id. */
+ tree tmpl;
+ /* The arguments of the concept-id. */
+ tree args;
+ /* The normal form of the concept-id. */
+ tree norm;
+};
+
+struct norm_hasher : ggc_ptr_hash<norm_entry>
+{
+ static hashval_t hash (norm_entry *e)
+ {
+ ++comparing_specializations;
+ hashval_t val = iterative_hash_template_arg (e->tmpl, 0);
+ val = iterative_hash_template_arg (e->args, val);
+ --comparing_specializations;
+ return val;
+ }
+
+ static bool equal (norm_entry *e1, norm_entry *e2)
+ {
+ ++comparing_specializations;
+ bool eq = e1->tmpl == e2->tmpl
+ && template_args_equal (e1->args, e2->args);
+ --comparing_specializations;
+ return eq;
+ }
+};
+
+static GTY((deletable)) hash_table<norm_hasher> *norm_cache;
+
+/* Normalize the concept check CHECK where ARGS are the
+ arguments to be substituted into CHECK's arguments. */
+
static tree
normalize_concept_check (tree check, tree args, norm_info info)
{
targs = tsubst_template_args (targs, args, info.complain, info.in_decl);
if (targs == error_mark_node)
return error_mark_node;
+ if (template_args_equal (targs, generic_targs_for (tmpl)))
+ /* Canonicalize generic arguments as NULL_TREE, as an optimization. */
+ targs = NULL_TREE;
/* Build the substitution for the concept definition. */
tree parms = TREE_VALUE (DECL_TEMPLATE_PARMS (tmpl));
- /* Turn on template processing; coercing non-type template arguments
- will automatically assume they're non-dependent. */
- ++processing_template_decl;
- tree subst = coerce_template_parms (parms, targs, tmpl);
- --processing_template_decl;
- if (subst == error_mark_node)
+ if (targs && args)
+ /* As an optimization, coerce the arguments only if necessary
+ (i.e. if they were substituted). */
+ targs = coerce_template_parms (parms, targs, tmpl, tf_none);
+ if (targs == error_mark_node)
return error_mark_node;
- /* The concept may have been ill-formed. */
- tree def = get_concept_definition (DECL_TEMPLATE_RESULT (tmpl));
- if (def == error_mark_node)
- return error_mark_node;
+ if (!norm_cache)
+ norm_cache = hash_table<norm_hasher>::create_ggc (31);
+ norm_entry *entry = nullptr;
+ if (!info.generate_diagnostics ())
+ {
+ /* Cache the normal form of the substituted concept-id (when not
+ diagnosing). */
+ norm_entry elt = {tmpl, targs, NULL_TREE};
+ norm_entry **slot = norm_cache->find_slot (&elt, INSERT);
+ if (*slot)
+ return (*slot)->norm;
+ entry = ggc_alloc<norm_entry> ();
+ *entry = elt;
+ *slot = entry;
+ }
+ tree def = get_concept_definition (DECL_TEMPLATE_RESULT (tmpl));
info.update_context (check, args);
- return normalize_expression (def, subst, info);
+ tree norm = normalize_expression (def, targs, info);
+ if (entry)
+ entry->norm = norm;
+ return norm;
}
/* Used by normalize_atom to cache ATOMIC_CONSTRs. */
/* Returns the normal form of TMPL's definition. */
static tree
-normalize_concept_definition (tree tmpl, bool diag = false)
+normalize_concept_definition (tree tmpl, bool diag)
{
+ if (!norm_cache)
+ norm_cache = hash_table<norm_hasher>::create_ggc (31);
+ norm_entry entry = {tmpl, NULL_TREE, NULL_TREE};
+
if (!diag)
- if (tree *p = hash_map_safe_get (normalized_map, tmpl))
- return *p;
+ if (norm_entry *found = norm_cache->find (&entry))
+ return found->norm;
- gcc_assert (concept_definition_p (tmpl));
- if (OVL_P (tmpl))
- tmpl = OVL_FIRST (tmpl);
gcc_assert (TREE_CODE (tmpl) == TEMPLATE_DECL);
tree def = get_concept_definition (DECL_TEMPLATE_RESULT (tmpl));
++processing_template_decl;
--processing_template_decl;
if (!diag)
- hash_map_safe_put<hm_ggc> (normalized_map, tmpl, norm);
+ {
+ norm_entry **slot = norm_cache->find_slot (&entry, INSERT);
+ entry.norm = norm;
+ *slot = ggc_alloc<norm_entry> ();
+ **slot = entry;
+ }
return norm;
}
for declaration matching. */
tree
-maybe_substitute_reqs_for (tree reqs, const_tree decl_)
+maybe_substitute_reqs_for (tree reqs, const_tree decl)
{
if (reqs == NULL_TREE)
return NULL_TREE;
- tree decl = CONST_CAST_TREE (decl_);
- tree result = STRIP_TEMPLATE (decl);
-
- if (DECL_UNIQUE_FRIEND_P (result))
+ decl = STRIP_TEMPLATE (decl);
+ if (DECL_UNIQUE_FRIEND_P (decl) && DECL_TEMPLATE_INFO (decl))
{
- tree tmpl = decl;
- if (TREE_CODE (decl) != TEMPLATE_DECL)
- tmpl = DECL_TI_TEMPLATE (result);
-
- tree gargs = generic_targs_for (tmpl);
+ tree tmpl = DECL_TI_TEMPLATE (decl);
+ tree outer_args = outer_template_args (decl);
processing_template_decl_sentinel s;
- if (uses_template_parms (gargs))
+ if (PRIMARY_TEMPLATE_P (tmpl)
+ || uses_template_parms (outer_args))
++processing_template_decl;
- reqs = tsubst_constraint (reqs, gargs,
+ reqs = tsubst_constraint (reqs, outer_args,
tf_warning_or_error, NULL_TREE);
}
return reqs;
{
gcc_assert (standard_concept_p (tmpl));
gcc_assert (TREE_CODE (tmpl) == TEMPLATE_DECL);
+ if (TREE_DEPRECATED (DECL_TEMPLATE_RESULT (tmpl)))
+ warn_deprecated_use (DECL_TEMPLATE_RESULT (tmpl), NULL_TREE);
tree parms = INNERMOST_TEMPLATE_PARMS (DECL_TEMPLATE_PARMS (tmpl));
args = coerce_template_parms (parms, args, tmpl, complain);
if (args == error_mark_node)
check = ovl_make (tmpl);
check = build_concept_check (check, arg, args, tf_warning_or_error);
- /* Make the check a fold-expression if needed. */
+ /* Make the check a fold-expression if needed.
+ Use UNKNOWN_LOCATION so write_template_args can tell the
+ difference between this and a fold the user wrote. */
if (apply_to_each_p && declared_pack_p)
- check = finish_left_unary_fold_expr (check, TRUTH_ANDIF_EXPR);
+ check = finish_left_unary_fold_expr (UNKNOWN_LOCATION,
+ check, TRUTH_ANDIF_EXPR);
return check;
}
static tree
tsubst_valid_expression_requirement (tree t, tree args, sat_info info)
{
- tree r = tsubst_expr (t, args, tf_none, info.in_decl, false);
+ tree r = tsubst_expr (t, args, tf_none, info.in_decl);
if (convert_to_void (r, ICV_STATEMENT, tf_none) != error_mark_node)
return r;
{
inform (loc, "the required expression %qE is invalid, because", t);
if (r == error_mark_node)
- tsubst_expr (t, args, info.complain, info.in_decl, false);
+ tsubst_expr (t, args, info.complain, info.in_decl);
else
convert_to_void (r, ICV_STATEMENT, info.complain);
}
}
else if (info.noisy ())
{
- r = tsubst_expr (t, args, info.complain, info.in_decl, false);
+ r = tsubst_expr (t, args, info.complain, info.in_decl);
convert_to_void (r, ICV_STATEMENT, info.complain);
}
{
local_specialization_stack stack (lss_copy);
+ /* We need to check access during the substitution. */
+ deferring_access_check_sentinel acs (dk_no_deferred);
+
/* A requires-expression is an unevaluated context. */
cp_unevaluated u;
if (TREE_CODE (new_arg) == TYPE_ARGUMENT_PACK)
{
tree pack_args = ARGUMENT_PACK_ARGS (new_arg);
- for (int i = 0; i < TREE_VEC_LENGTH (pack_args); i++)
- {
- tree& pack_arg = TREE_VEC_ELT (pack_args, i);
- if (TYPE_P (pack_arg))
- pack_arg = canonicalize_type_argument (pack_arg, complain);
- }
+ for (tree& pack_arg : tree_vec_range (pack_args))
+ if (TYPE_P (pack_arg))
+ pack_arg = canonicalize_type_argument (pack_arg, complain);
}
if (new_arg == error_mark_node)
return error_mark_node;
{
static hashval_t hash (sat_entry *e)
{
+ auto cso = make_temp_override (comparing_specializations);
+ ++comparing_specializations;
+
if (ATOMIC_CONSTR_MAP_INSTANTIATED_P (e->atom))
{
/* Atoms with instantiated mappings are built during satisfaction.
static bool equal (sat_entry *e1, sat_entry *e2)
{
+ auto cso = make_temp_override (comparing_specializations);
+ ++comparing_specializations;
+
if (ATOMIC_CONSTR_MAP_INSTANTIATED_P (e1->atom)
!= ATOMIC_CONSTR_MAP_INSTANTIATED_P (e2->atom))
return false;
*slot = entry;
}
else
- /* We shouldn't get here, but if we do, let's just leave 'entry'
- empty, effectively disabling the cache. */
- return;
+ {
+ /* We're evaluating this atom for the first time, and doing so noisily.
+ This shouldn't happen outside of error recovery situations involving
+ unstable satisfaction. Let's just leave 'entry' empty, effectively
+ disabling the cache, and remove the empty slot. */
+ gcc_checking_assert (seen_error ());
+ /* Appease hash_table::check_complete_insertion. */
+ *slot = ggc_alloc<sat_entry> ();
+ sat_cache->clear_slot (slot);
+ }
}
/* Returns the cached satisfaction result if we have one and we're not
if (entry->evaluating)
{
/* If we get here, it means satisfaction is self-recursive. */
- gcc_checking_assert (!entry->result);
+ gcc_checking_assert (!entry->result || seen_error ());
if (info.noisy ())
error_at (EXPR_LOCATION (ATOMIC_CONSTR_EXPR (entry->atom)),
"satisfaction of atomic constraint %qE depends on itself",
constraint-expressions of a declaration. */
processing_constraint_expression_sentinel s;
cp_unevaluated u;
- tree expr = tsubst_expr (t, args, complain, in_decl, false);
+ tree expr = tsubst_expr (t, args, complain, in_decl);
return expr;
}
/* Apply the parameter mapping (i.e., just substitute). */
tree expr = ATOMIC_CONSTR_EXPR (t);
- tree result = tsubst_expr (expr, args, quiet.complain, quiet.in_decl, false);
+ tree result = tsubst_expr (expr, args, quiet.complain, quiet.in_decl);
if (result == error_mark_node)
{
/* If substitution results in an invalid type or expression, the constraint
is not satisfied. Replay the substitution. */
if (info.diagnose_unsatisfaction_p ())
- tsubst_expr (expr, args, info.complain, info.in_decl, false);
+ tsubst_expr (expr, args, info.complain, info.in_decl);
return cache.save (inst_cache.save (boolean_false_node));
}
}
else
{
- result = maybe_constant_value (result, NULL_TREE,
- /*manifestly_const_eval=*/true);
+ result = maybe_constant_value (result, NULL_TREE, mce_true);
if (!TREE_CONSTANT (result))
result = error_mark_node;
}
args = regen_args;
}
- /* If any arguments depend on template parameters, we can't
- check constraints. Pretend they're satisfied for now. */
- if (uses_template_parms (args))
+ /* If the innermost arguments are dependent, or if the outer arguments
+ are dependent and are needed by the constraints, we can't check
+ satisfaction yet so pretend they're satisfied for now. */
+ if (uses_template_parms (args)
+ && ((DECL_TEMPLATE_INFO (t)
+ && PRIMARY_TEMPLATE_P (DECL_TI_TEMPLATE (t))
+ && (TMPL_ARGS_DEPTH (args) == 1
+ || uses_template_parms (INNERMOST_TEMPLATE_ARGS (args))))
+ || uses_outer_template_parms_in_constraints (t)))
return boolean_true_node;
/* Get the normalized constraints. */
else
args = add_outermost_template_args (t, args);
- /* If any arguments depend on template parameters, we can't
- check constraints. Pretend they're satisfied for now. */
- if (uses_template_parms (args))
+ /* If the innermost arguments are dependent, or if the outer arguments
+ are dependent and are needed by the constraints, we can't check
+ satisfaction yet so pretend they're satisfied for now. */
+ if (uses_template_parms (args)
+ && (TMPL_ARGS_DEPTH (args) == 1
+ || uses_template_parms (INNERMOST_TEMPLATE_ARGS (args))
+ || uses_outer_template_parms_in_constraints (t)))
return boolean_true_node;
tree result = boolean_true_node;
/* Build a "fake" version of the instantiated trait, so we can
get the instantiated types from result. */
++processing_template_decl;
- expr = tsubst_expr (expr, args, tf_none, NULL_TREE, false);
+ expr = tsubst_expr (expr, args, tf_none, NULL_TREE);
--processing_template_decl;
tree t1 = TRAIT_EXPR_TYPE1 (expr);
tree t2 = TRAIT_EXPR_TYPE2 (expr);
+ if (t2 && TREE_CODE (t2) == TREE_VEC)
+ {
+ /* Convert the TREE_VEC of arguments into a TREE_LIST, since we can't
+ directly print a TREE_VEC but we can a TREE_LIST via the E format
+ specifier. */
+ tree list = NULL_TREE;
+ for (tree t : tree_vec_range (t2))
+ list = tree_cons (NULL_TREE, t, list);
+ t2 = nreverse (list);
+ }
switch (TRAIT_EXPR_KIND (expr))
{
case CPTK_HAS_NOTHROW_ASSIGN:
- inform (loc, " %qT is not %<nothrow%> copy assignable", t1);
+ inform (loc, " %qT is not nothrow copy assignable", t1);
break;
case CPTK_HAS_NOTHROW_CONSTRUCTOR:
- inform (loc, " %qT is not %<nothrow%> default constructible", t1);
+ inform (loc, " %qT is not nothrow default constructible", t1);
break;
case CPTK_HAS_NOTHROW_COPY:
- inform (loc, " %qT is not %<nothrow%> copy constructible", t1);
+ inform (loc, " %qT is not nothrow copy constructible", t1);
break;
case CPTK_HAS_TRIVIAL_ASSIGN:
inform (loc, " %qT is not trivially copy assignable", t1);
case CPTK_HAS_TRIVIAL_DESTRUCTOR:
inform (loc, " %qT is not trivially destructible", t1);
break;
+ case CPTK_HAS_UNIQUE_OBJ_REPRESENTATIONS:
+ inform (loc, " %qT does not have unique object representations", t1);
+ break;
case CPTK_HAS_VIRTUAL_DESTRUCTOR:
inform (loc, " %qT does not have a virtual destructor", t1);
break;
case CPTK_IS_ABSTRACT:
inform (loc, " %qT is not an abstract class", t1);
break;
+ case CPTK_IS_AGGREGATE:
+ inform (loc, " %qT is not an aggregate", t1);
+ break;
+ case CPTK_IS_ARRAY:
+ inform (loc, " %qT is not an array", t1);
+ break;
+ case CPTK_IS_ASSIGNABLE:
+ inform (loc, " %qT is not assignable from %qT", t1, t2);
+ break;
case CPTK_IS_BASE_OF:
inform (loc, " %qT is not a base of %qT", t1, t2);
break;
+ case CPTK_IS_BOUNDED_ARRAY:
+ inform (loc, " %qT is not a bounded array", t1);
+ break;
case CPTK_IS_CLASS:
inform (loc, " %qT is not a class", t1);
break;
+ case CPTK_IS_CONSTRUCTIBLE:
+ if (!t2)
+ inform (loc, " %qT is not default constructible", t1);
+ else
+ inform (loc, " %qT is not constructible from %qE", t1, t2);
+ break;
+ case CPTK_IS_CONVERTIBLE:
+ inform (loc, " %qT is not convertible from %qE", t2, t1);
+ break;
case CPTK_IS_EMPTY:
inform (loc, " %qT is not an empty class", t1);
break;
case CPTK_IS_FINAL:
inform (loc, " %qT is not a final class", t1);
break;
+ case CPTK_IS_FUNCTION:
+ inform (loc, " %qT is not a function", t1);
+ break;
case CPTK_IS_LAYOUT_COMPATIBLE:
inform (loc, " %qT is not layout compatible with %qT", t1, t2);
break;
case CPTK_IS_LITERAL_TYPE:
inform (loc, " %qT is not a literal type", t1);
break;
+ case CPTK_IS_MEMBER_FUNCTION_POINTER:
+ inform (loc, " %qT is not a member function pointer", t1);
+ break;
+ case CPTK_IS_MEMBER_OBJECT_POINTER:
+ inform (loc, " %qT is not a member object pointer", t1);
+ break;
+ case CPTK_IS_MEMBER_POINTER:
+ inform (loc, " %qT is not a member pointer", t1);
+ break;
+ case CPTK_IS_NOTHROW_ASSIGNABLE:
+ inform (loc, " %qT is not nothrow assignable from %qT", t1, t2);
+ break;
+ case CPTK_IS_NOTHROW_CONSTRUCTIBLE:
+ if (!t2)
+ inform (loc, " %qT is not nothrow default constructible", t1);
+ else
+ inform (loc, " %qT is not nothrow constructible from %qE", t1, t2);
+ break;
+ case CPTK_IS_NOTHROW_CONVERTIBLE:
+ inform (loc, " %qT is not nothrow convertible from %qE", t2, t1);
+ break;
+ case CPTK_IS_OBJECT:
+ inform (loc, " %qT is not an object type", t1);
+ break;
case CPTK_IS_POINTER_INTERCONVERTIBLE_BASE_OF:
inform (loc, " %qT is not pointer-interconvertible base of %qT",
t1, t2);
case CPTK_IS_POLYMORPHIC:
inform (loc, " %qT is not a polymorphic type", t1);
break;
- case CPTK_IS_SAME_AS:
+ case CPTK_IS_REFERENCE:
+ inform (loc, " %qT is not a reference", t1);
+ break;
+ case CPTK_IS_SAME:
inform (loc, " %qT is not the same as %qT", t1, t2);
break;
+ case CPTK_IS_SCOPED_ENUM:
+ inform (loc, " %qT is not a scoped enum", t1);
+ break;
case CPTK_IS_STD_LAYOUT:
inform (loc, " %qT is not an standard layout type", t1);
break;
case CPTK_IS_TRIVIAL:
inform (loc, " %qT is not a trivial type", t1);
break;
+ case CPTK_IS_TRIVIALLY_ASSIGNABLE:
+ inform (loc, " %qT is not trivially assignable from %qT", t1, t2);
+ break;
+ case CPTK_IS_TRIVIALLY_CONSTRUCTIBLE:
+ if (!t2)
+ inform (loc, " %qT is not trivially default constructible", t1);
+ else
+ inform (loc, " %qT is not trivially constructible from %qE", t1, t2);
+ break;
+ case CPTK_IS_TRIVIALLY_COPYABLE:
+ inform (loc, " %qT is not trivially copyable", t1);
+ break;
case CPTK_IS_UNION:
inform (loc, " %qT is not a union", t1);
break;
- default:
+ case CPTK_REF_CONSTRUCTS_FROM_TEMPORARY:
+ inform (loc, " %qT is not a reference that binds to a temporary "
+ "object of type %qT (direct-initialization)", t1, t2);
+ break;
+ case CPTK_REF_CONVERTS_FROM_TEMPORARY:
+ inform (loc, " %qT is not a reference that binds to a temporary "
+ "object of type %qT (copy-initialization)", t1, t2);
+ break;
+ case CPTK_IS_DEDUCIBLE:
+ inform (loc, " %qD is not deducible from %qT", t1, t2);
+ break;
+#define DEFTRAIT_TYPE(CODE, NAME, ARITY) \
+ case CPTK_##CODE:
+#include "cp-trait.def"
+#undef DEFTRAIT_TYPE
+ /* Type-yielding traits aren't expressions. */
gcc_unreachable ();
+ /* We deliberately omit the default case so that when adding a new
+ trait we'll get reminded (by way of a warning) to handle it here. */
}
}