| /* Processing rules for constraints. |
| Copyright (C) 2013-2022 Free Software Foundation, Inc. |
| Contributed by Andrew Sutton (andrew.n.sutton@gmail.com) |
| |
| This file is part of GCC. |
| |
| GCC is free software; you can redistribute it and/or modify |
| it under the terms of the GNU General Public License as published by |
| the Free Software Foundation; either version 3, or (at your option) |
| any later version. |
| |
| GCC is distributed in the hope that it will be useful, |
| but WITHOUT ANY WARRANTY; without even the implied warranty of |
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| GNU General Public License for more details. |
| |
| You should have received a copy of the GNU General Public License |
| along with GCC; see the file COPYING3. If not see |
| <http://www.gnu.org/licenses/>. */ |
| |
| #include "config.h" |
| #include "system.h" |
| #include "coretypes.h" |
| #include "tm.h" |
| #include "timevar.h" |
| #include "hash-set.h" |
| #include "machmode.h" |
| #include "vec.h" |
| #include "double-int.h" |
| #include "input.h" |
| #include "alias.h" |
| #include "symtab.h" |
| #include "wide-int.h" |
| #include "inchash.h" |
| #include "tree.h" |
| #include "stringpool.h" |
| #include "attribs.h" |
| #include "intl.h" |
| #include "flags.h" |
| #include "cp-tree.h" |
| #include "c-family/c-common.h" |
| #include "c-family/c-objc.h" |
| #include "cp-objcp-common.h" |
| #include "tree-inline.h" |
| #include "decl.h" |
| #include "toplev.h" |
| #include "type-utils.h" |
| |
| static tree satisfaction_value (tree t); |
| |
| /* When we're parsing or substuting a constraint expression, we have slightly |
| different expression semantics. In particular, we don't want to reduce a |
| concept-id to a satisfaction value. */ |
| |
| processing_constraint_expression_sentinel:: |
| processing_constraint_expression_sentinel () |
| { |
| ++scope_chain->x_processing_constraint; |
| } |
| |
| processing_constraint_expression_sentinel:: |
| ~processing_constraint_expression_sentinel () |
| { |
| --scope_chain->x_processing_constraint; |
| } |
| |
| bool |
| processing_constraint_expression_p () |
| { |
| return scope_chain->x_processing_constraint != 0; |
| } |
| |
| /*--------------------------------------------------------------------------- |
| Constraint expressions |
| ---------------------------------------------------------------------------*/ |
| |
| /* Information provided to substitution. */ |
| |
| struct subst_info |
| { |
| subst_info (tsubst_flags_t cmp, tree in) |
| : complain (cmp), in_decl (in) |
| { } |
| |
| /* True if we should not diagnose errors. */ |
| bool quiet() const |
| { |
| return complain == tf_none; |
| } |
| |
| /* True if we should diagnose errors. */ |
| bool noisy() const |
| { |
| return !quiet (); |
| } |
| |
| tsubst_flags_t complain; |
| tree in_decl; |
| }; |
| |
| /* Provides additional context for satisfaction. |
| |
| During satisfaction: |
| - The flag noisy() controls whether to diagnose ill-formed satisfaction, |
| such as the satisfaction value of an atom being non-bool or non-constant. |
| - The flag diagnose_unsatisfaction_p() controls whether to additionally |
| explain why a constraint is not satisfied. |
| - We enter satisfaction with noisy+unsat from diagnose_constraints. |
| - We enter satisfaction with noisy-unsat from the replay inside |
| constraint_satisfaction_value. |
| - We enter satisfaction quietly (both flags cleared) from |
| constraints_satisfied_p. |
| |
| During evaluation of a requires-expression: |
| - The flag noisy() controls whether to diagnose ill-formed types and |
| expressions inside its requirements. |
| - The flag diagnose_unsatisfaction_p() controls whether to additionally |
| explain why the requires-expression evaluates to false. |
| - We enter tsubst_requires_expr with noisy+unsat from |
| diagnose_atomic_constraint and potentially from |
| satisfy_nondeclaration_constraints. |
| - We enter tsubst_requires_expr with noisy-unsat from |
| cp_parser_requires_expression when processing a requires-expression that |
| appears outside a template. |
| - We enter tsubst_requires_expr quietly (both flags cleared) when |
| substituting through a requires-expression as part of template |
| instantiation. */ |
| |
| struct sat_info : subst_info |
| { |
| sat_info (tsubst_flags_t cmp, tree in, bool diag_unsat = false) |
| : subst_info (cmp, in), diagnose_unsatisfaction (diag_unsat) |
| { |
| if (diagnose_unsatisfaction_p ()) |
| gcc_checking_assert (noisy ()); |
| } |
| |
| /* True if we should diagnose the cause of satisfaction failure. |
| Implies noisy(). */ |
| bool |
| diagnose_unsatisfaction_p () const |
| { |
| return diagnose_unsatisfaction; |
| } |
| |
| bool diagnose_unsatisfaction; |
| }; |
| |
| static tree constraint_satisfaction_value (tree, tree, sat_info); |
| |
| /* True if T is known to be some type other than bool. Note that this |
| is false for dependent types and errors. */ |
| |
| static inline bool |
| known_non_bool_p (tree t) |
| { |
| return (t && !WILDCARD_TYPE_P (t) && TREE_CODE (t) != BOOLEAN_TYPE); |
| } |
| |
| static bool |
| check_constraint_atom (cp_expr expr) |
| { |
| if (known_non_bool_p (TREE_TYPE (expr))) |
| { |
| error_at (expr.get_location (), |
| "constraint expression does not have type %<bool%>"); |
| return false; |
| } |
| |
| /* Check that we're using function concepts correctly. */ |
| if (concept_check_p (expr)) |
| { |
| tree id = unpack_concept_check (expr); |
| tree tmpl = TREE_OPERAND (id, 0); |
| if (OVL_P (tmpl) && TREE_CODE (expr) == TEMPLATE_ID_EXPR) |
| { |
| error_at (EXPR_LOC_OR_LOC (expr, input_location), |
| "function concept must be called"); |
| return false; |
| } |
| } |
| |
| return true; |
| } |
| |
| static bool |
| check_constraint_operands (location_t, cp_expr lhs, cp_expr rhs) |
| { |
| return check_constraint_atom (lhs) && check_constraint_atom (rhs); |
| } |
| |
| /* Validate the semantic properties of the constraint expression. */ |
| |
| static cp_expr |
| finish_constraint_binary_op (location_t loc, |
| tree_code code, |
| cp_expr lhs, |
| cp_expr rhs) |
| { |
| gcc_assert (processing_constraint_expression_p ()); |
| if (lhs == error_mark_node || rhs == error_mark_node) |
| return error_mark_node; |
| if (!check_constraint_operands (loc, lhs, rhs)) |
| return error_mark_node; |
| cp_expr expr |
| = build_min_nt_loc (loc, code, lhs.get_value (), rhs.get_value ()); |
| expr.set_range (lhs.get_start (), rhs.get_finish ()); |
| return expr; |
| } |
| |
| cp_expr |
| finish_constraint_or_expr (location_t loc, cp_expr lhs, cp_expr rhs) |
| { |
| return finish_constraint_binary_op (loc, TRUTH_ORIF_EXPR, lhs, rhs); |
| } |
| |
| cp_expr |
| finish_constraint_and_expr (location_t loc, cp_expr lhs, cp_expr rhs) |
| { |
| return finish_constraint_binary_op (loc, TRUTH_ANDIF_EXPR, lhs, rhs); |
| } |
| |
| cp_expr |
| finish_constraint_primary_expr (cp_expr expr) |
| { |
| if (expr == error_mark_node) |
| return error_mark_node; |
| if (!check_constraint_atom (expr)) |
| return cp_expr (error_mark_node, expr.get_location ()); |
| return expr; |
| } |
| |
| /* Combine two constraint-expressions with a logical-and. */ |
| |
| tree |
| combine_constraint_expressions (tree lhs, tree rhs) |
| { |
| processing_constraint_expression_sentinel pce; |
| if (!lhs) |
| return rhs; |
| if (!rhs) |
| return lhs; |
| return finish_constraint_and_expr (input_location, lhs, rhs); |
| } |
| |
| /* Extract the template-id from a concept check. For standard and variable |
| checks, this is simply T. For function concept checks, this is the |
| called function. */ |
| |
| tree |
| unpack_concept_check (tree t) |
| { |
| gcc_assert (concept_check_p (t)); |
| |
| if (TREE_CODE (t) == CALL_EXPR) |
| t = CALL_EXPR_FN (t); |
| |
| gcc_assert (TREE_CODE (t) == TEMPLATE_ID_EXPR); |
| return t; |
| } |
| |
| /* Extract the TEMPLATE_DECL from a concept check. */ |
| |
| tree |
| get_concept_check_template (tree t) |
| { |
| tree id = unpack_concept_check (t); |
| tree tmpl = TREE_OPERAND (id, 0); |
| if (OVL_P (tmpl)) |
| tmpl = OVL_FIRST (tmpl); |
| return tmpl; |
| } |
| |
| /*--------------------------------------------------------------------------- |
| Resolution of qualified concept names |
| ---------------------------------------------------------------------------*/ |
| |
| /* This facility is used to resolve constraint checks from requirement |
| expressions. A constraint check is a call to a function template declared |
| with the keyword 'concept'. |
| |
| The result of resolution is a pair (a TREE_LIST) whose value is the |
| matched declaration, and whose purpose contains the coerced template |
| arguments that can be substituted into the call. */ |
| |
| /* Given an overload set OVL, try to find a unique definition that can be |
| instantiated by the template arguments ARGS. |
| |
| This function is not called for arbitrary call expressions. In particular, |
| the call expression must be written with explicit template arguments |
| and no function arguments. For example: |
| |
| f<T, U>() |
| |
| If a single match is found, this returns a TREE_LIST whose VALUE |
| is the constraint function (not the template), and its PURPOSE is |
| the complete set of arguments substituted into the parameter list. */ |
| |
| static tree |
| resolve_function_concept_overload (tree ovl, tree args) |
| { |
| int nerrs = 0; |
| tree cands = NULL_TREE; |
| for (lkp_iterator iter (ovl); iter; ++iter) |
| { |
| tree tmpl = *iter; |
| if (TREE_CODE (tmpl) != TEMPLATE_DECL) |
| continue; |
| |
| /* Don't try to deduce checks for non-concepts. We often end up trying |
| to resolve constraints in functional casts as part of a |
| postfix-expression. We can save time and headaches by not |
| instantiating those declarations. |
| |
| NOTE: This masks a potential error, caused by instantiating |
| non-deduced contexts using placeholder arguments. */ |
| tree fn = DECL_TEMPLATE_RESULT (tmpl); |
| if (DECL_ARGUMENTS (fn)) |
| continue; |
| if (!DECL_DECLARED_CONCEPT_P (fn)) |
| continue; |
| |
| /* 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 (subst == error_mark_node) |
| ++nerrs; |
| else |
| cands = tree_cons (subst, fn, cands); |
| } |
| --processing_template_decl; |
| } |
| |
| if (!cands) |
| /* We either had no candidates or failed deductions. */ |
| return nerrs ? error_mark_node : NULL_TREE; |
| else if (TREE_CHAIN (cands)) |
| /* There are multiple candidates. */ |
| return error_mark_node; |
| |
| return cands; |
| } |
| |
| /* Determine if the call expression CALL is a constraint check, and |
| return the concept declaration and arguments being checked. If CALL |
| does not denote a constraint check, return NULL. */ |
| |
| tree |
| resolve_function_concept_check (tree call) |
| { |
| gcc_assert (TREE_CODE (call) == CALL_EXPR); |
| |
| /* A constraint check must be only a template-id expression. |
| If it's a call to a base-link, its function(s) should be a |
| template-id expression. If this is not a template-id, then |
| it cannot be a concept-check. */ |
| tree target = CALL_EXPR_FN (call); |
| if (BASELINK_P (target)) |
| target = BASELINK_FUNCTIONS (target); |
| if (TREE_CODE (target) != TEMPLATE_ID_EXPR) |
| return NULL_TREE; |
| |
| /* Get the overload set and template arguments and try to |
| resolve the target. */ |
| tree ovl = TREE_OPERAND (target, 0); |
| |
| /* This is a function call of a variable concept... ill-formed. */ |
| if (TREE_CODE (ovl) == TEMPLATE_DECL) |
| { |
| error_at (location_of (call), |
| "function call of variable concept %qE", call); |
| return error_mark_node; |
| } |
| |
| tree args = TREE_OPERAND (target, 1); |
| return resolve_function_concept_overload (ovl, args); |
| } |
| |
| /* Returns a pair containing the checked concept and its associated |
| prototype parameter. The result is a TREE_LIST whose TREE_VALUE |
| is the concept (non-template) and whose TREE_PURPOSE contains |
| the converted template arguments, including the deduced prototype |
| parameter (in position 0). */ |
| |
| tree |
| resolve_concept_check (tree check) |
| { |
| gcc_assert (concept_check_p (check)); |
| tree id = unpack_concept_check (check); |
| tree tmpl = TREE_OPERAND (id, 0); |
| |
| /* If this is an overloaded function concept, perform overload |
| resolution (this only happens when deducing prototype parameters |
| and template introductions). */ |
| if (TREE_CODE (tmpl) == OVERLOAD) |
| { |
| if (OVL_CHAIN (tmpl)) |
| return resolve_function_concept_check (check); |
| tmpl = OVL_FIRST (tmpl); |
| } |
| |
| 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); |
| --processing_template_decl; |
| if (result == error_mark_node) |
| return error_mark_node; |
| return build_tree_list (result, DECL_TEMPLATE_RESULT (tmpl)); |
| } |
| |
| /* Given a call expression or template-id expression to a concept EXPR |
| possibly including a wildcard, deduce the concept being checked and |
| the prototype parameter. Returns true if the constraint and prototype |
| can be deduced and false otherwise. Note that the CHECK and PROTO |
| arguments are set to NULL_TREE if this returns false. */ |
| |
| bool |
| deduce_constrained_parameter (tree expr, tree& check, tree& proto) |
| { |
| tree info = resolve_concept_check (expr); |
| if (info && info != error_mark_node) |
| { |
| check = TREE_VALUE (info); |
| tree arg = TREE_VEC_ELT (TREE_PURPOSE (info), 0); |
| if (ARGUMENT_PACK_P (arg)) |
| arg = TREE_VEC_ELT (ARGUMENT_PACK_ARGS (arg), 0); |
| proto = TREE_TYPE (arg); |
| return true; |
| } |
| |
| check = proto = NULL_TREE; |
| return false; |
| } |
| |
| /* Given a call expression or template-id expression to a concept, EXPR, |
| deduce the concept being checked and return the template arguments. |
| Returns NULL_TREE if deduction fails. */ |
| static tree |
| deduce_concept_introduction (tree check) |
| { |
| tree info = resolve_concept_check (check); |
| if (info && info != error_mark_node) |
| return TREE_PURPOSE (info); |
| return NULL_TREE; |
| } |
| |
| /* Build a constrained placeholder type where SPEC is a type-constraint. |
| SPEC can be anything were concept_definition_p is true. |
| |
| Returns a pair whose FIRST is the concept being checked and whose |
| SECOND is the prototype parameter. */ |
| |
| tree_pair |
| finish_type_constraints (tree spec, tree args, tsubst_flags_t complain) |
| { |
| gcc_assert (concept_definition_p (spec)); |
| |
| /* Build an initial concept check. */ |
| tree check = build_type_constraint (spec, args, complain); |
| if (check == error_mark_node) |
| return std::make_pair (error_mark_node, NULL_TREE); |
| |
| /* Extract the concept and prototype parameter from the check. */ |
| tree con; |
| tree proto; |
| if (!deduce_constrained_parameter (check, con, proto)) |
| return std::make_pair (error_mark_node, NULL_TREE); |
| |
| return std::make_pair (con, proto); |
| } |
| |
| /*--------------------------------------------------------------------------- |
| Expansion of concept definitions |
| ---------------------------------------------------------------------------*/ |
| |
| /* Returns the expression of a function concept. */ |
| |
| static tree |
| get_returned_expression (tree fn) |
| { |
| /* Extract the body of the function minus the return expression. */ |
| tree body = DECL_SAVED_TREE (fn); |
| if (!body) |
| return error_mark_node; |
| if (TREE_CODE (body) == BIND_EXPR) |
| body = BIND_EXPR_BODY (body); |
| if (TREE_CODE (body) != RETURN_EXPR) |
| return error_mark_node; |
| |
| return TREE_OPERAND (body, 0); |
| } |
| |
| /* Returns the initializer of a variable concept. */ |
| |
| static tree |
| get_variable_initializer (tree var) |
| { |
| tree init = DECL_INITIAL (var); |
| if (!init) |
| return error_mark_node; |
| if (BRACE_ENCLOSED_INITIALIZER_P (init) |
| && CONSTRUCTOR_NELTS (init) == 1) |
| init = CONSTRUCTOR_ELT (init, 0)->value; |
| return init; |
| } |
| |
| /* Returns the definition of a variable or function concept. */ |
| |
| static tree |
| get_concept_definition (tree decl) |
| { |
| if (TREE_CODE (decl) == OVERLOAD) |
| decl = OVL_FIRST (decl); |
| |
| if (TREE_CODE (decl) == TEMPLATE_DECL) |
| decl = DECL_TEMPLATE_RESULT (decl); |
| |
| if (TREE_CODE (decl) == CONCEPT_DECL) |
| return DECL_INITIAL (decl); |
| if (VAR_P (decl)) |
| return get_variable_initializer (decl); |
| if (TREE_CODE (decl) == FUNCTION_DECL) |
| return get_returned_expression (decl); |
| gcc_unreachable (); |
| } |
| |
| /*--------------------------------------------------------------------------- |
| Normalization of expressions |
| |
| This set of functions will transform an expression into a constraint |
| in a sequence of steps. |
| ---------------------------------------------------------------------------*/ |
| |
| void |
| debug_parameter_mapping (tree map) |
| { |
| for (tree p = map; p; p = TREE_CHAIN (p)) |
| { |
| tree parm = TREE_VALUE (p); |
| tree arg = TREE_PURPOSE (p); |
| if (TYPE_P (parm)) |
| verbatim ("MAP %qD TO %qT", TEMPLATE_TYPE_DECL (parm), arg); |
| else |
| verbatim ("MAP %qD TO %qE", TEMPLATE_PARM_DECL (parm), arg); |
| // debug_tree (parm); |
| // debug_tree (arg); |
| } |
| } |
| |
| void |
| debug_argument_list (tree args) |
| { |
| for (int i = 0; i < TREE_VEC_LENGTH (args); ++i) |
| { |
| tree arg = TREE_VEC_ELT (args, i); |
| if (TYPE_P (arg)) |
| verbatim ("argument %qT", arg); |
| else |
| verbatim ("argument %qE", arg); |
| } |
| } |
| |
| /* Associate each parameter in PARMS with its corresponding template |
| argument in ARGS. */ |
| |
| static tree |
| map_arguments (tree parms, tree args) |
| { |
| for (tree p = parms; p; p = TREE_CHAIN (p)) |
| if (args) |
| { |
| int level; |
| int index; |
| template_parm_level_and_index (TREE_VALUE (p), &level, &index); |
| TREE_PURPOSE (p) = TMPL_ARG (args, level, index); |
| } |
| else |
| TREE_PURPOSE (p) = template_parm_to_arg (p); |
| |
| return parms; |
| } |
| |
| /* Build the parameter mapping for EXPR using ARGS, where CTX_PARMS |
| are the template parameters in scope for EXPR. */ |
| |
| static tree |
| build_parameter_mapping (tree expr, tree args, tree ctx_parms) |
| { |
| tree parms = find_template_parameters (expr, ctx_parms); |
| tree map = map_arguments (parms, args); |
| return map; |
| } |
| |
| /* True if the parameter mappings of two atomic constraints formed |
| from the same expression are equivalent. */ |
| |
| static bool |
| parameter_mapping_equivalent_p (tree t1, tree t2) |
| { |
| tree map1 = ATOMIC_CONSTR_MAP (t1); |
| tree map2 = ATOMIC_CONSTR_MAP (t2); |
| while (map1 && map2) |
| { |
| gcc_checking_assert (TREE_VALUE (map1) == TREE_VALUE (map2)); |
| tree arg1 = TREE_PURPOSE (map1); |
| tree arg2 = TREE_PURPOSE (map2); |
| if (!template_args_equal (arg1, arg2)) |
| return false; |
| map1 = TREE_CHAIN (map1); |
| map2 = TREE_CHAIN (map2); |
| } |
| gcc_checking_assert (!map1 && !map2); |
| return true; |
| } |
| |
| /* Provides additional context for normalization. */ |
| |
| struct norm_info : subst_info |
| { |
| explicit norm_info (tsubst_flags_t cmp) |
| : norm_info (NULL_TREE, cmp) |
| {} |
| |
| /* Construct a top-level context for DECL. */ |
| |
| norm_info (tree in_decl, tsubst_flags_t complain) |
| : subst_info (tf_warning_or_error | complain, in_decl) |
| { |
| if (in_decl) |
| { |
| initial_parms = DECL_TEMPLATE_PARMS (in_decl); |
| if (generate_diagnostics ()) |
| context = build_tree_list (NULL_TREE, in_decl); |
| } |
| else |
| initial_parms = current_template_parms; |
| } |
| |
| bool generate_diagnostics() const |
| { |
| return complain & tf_norm; |
| } |
| |
| void update_context(tree expr, tree args) |
| { |
| if (generate_diagnostics ()) |
| { |
| tree map = build_parameter_mapping (expr, args, ctx_parms ()); |
| context = tree_cons (map, expr, context); |
| } |
| in_decl = get_concept_check_template (expr); |
| } |
| |
| /* Returns the template parameters that are in scope for the current |
| normalization context. */ |
| |
| tree ctx_parms() |
| { |
| if (in_decl) |
| return DECL_TEMPLATE_PARMS (in_decl); |
| else |
| return initial_parms; |
| } |
| |
| /* Provides information about the source of a constraint. This is a |
| TREE_LIST whose VALUE is either a concept check or a constrained |
| declaration. The PURPOSE, for concept checks is a parameter mapping |
| for that check. */ |
| |
| tree context = NULL_TREE; |
| |
| /* The declaration whose constraints we're normalizing. The targets |
| of the parameter mapping of each atom will be in terms of the |
| template parameters of ORIG_DECL. */ |
| |
| tree initial_parms = NULL_TREE; |
| }; |
| |
| static tree normalize_expression (tree, tree, norm_info); |
| |
| /* Transform a logical-or or logical-and expression into either |
| a conjunction or disjunction. */ |
| |
| static tree |
| normalize_logical_operation (tree t, tree args, tree_code c, norm_info info) |
| { |
| tree t0 = normalize_expression (TREE_OPERAND (t, 0), args, info); |
| tree t1 = normalize_expression (TREE_OPERAND (t, 1), args, info); |
| |
| /* Build a new info object for the constraint. */ |
| tree ci = info.generate_diagnostics() |
| ? build_tree_list (t, info.context) |
| : NULL_TREE; |
| |
| return build2 (c, ci, t0, t1); |
| } |
| |
| static tree |
| normalize_concept_check (tree check, tree args, norm_info info) |
| { |
| tree id = unpack_concept_check (check); |
| tree tmpl = TREE_OPERAND (id, 0); |
| tree targs = TREE_OPERAND (id, 1); |
| |
| /* A function concept is wrapped in an overload. */ |
| if (TREE_CODE (tmpl) == OVERLOAD) |
| { |
| /* TODO: Can we diagnose this error during parsing? */ |
| if (TREE_CODE (check) == TEMPLATE_ID_EXPR) |
| error_at (EXPR_LOC_OR_LOC (check, input_location), |
| "function concept must be called"); |
| tmpl = OVL_FIRST (tmpl); |
| } |
| |
| /* Substitute through the arguments of the concept check. */ |
| if (args) |
| targs = tsubst_template_args (targs, args, info.complain, info.in_decl); |
| if (targs == error_mark_node) |
| return error_mark_node; |
| |
| /* 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) |
| 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; |
| |
| info.update_context (check, args); |
| return normalize_expression (def, subst, info); |
| } |
| |
| /* Used by normalize_atom to cache ATOMIC_CONSTRs. */ |
| |
| static GTY((deletable)) hash_table<atom_hasher> *atom_cache; |
| |
| /* The normal form of an atom depends on the expression. The normal |
| form of a function call to a function concept is a check constraint |
| for that concept. The normal form of a reference to a variable |
| concept is a check constraint for that concept. Otherwise, the |
| constraint is a predicate constraint. */ |
| |
| static tree |
| normalize_atom (tree t, tree args, norm_info info) |
| { |
| /* Concept checks are not atomic. */ |
| if (concept_check_p (t)) |
| return normalize_concept_check (t, args, info); |
| |
| /* Build the parameter mapping for the atom. */ |
| tree map = build_parameter_mapping (t, args, info.ctx_parms ()); |
| |
| /* Build a new info object for the atom. */ |
| tree ci = build_tree_list (t, info.context); |
| |
| tree atom = build1 (ATOMIC_CONSTR, ci, map); |
| |
| /* Remember whether the expression of this atomic constraint belongs to |
| a concept definition by inspecting in_decl, which should always be set |
| in this case either by norm_info::update_context (when recursing into a |
| concept-id during normalization) or by normalize_concept_definition |
| (when starting out with a concept-id). */ |
| if (info.in_decl && concept_definition_p (info.in_decl)) |
| ATOMIC_CONSTR_EXPR_FROM_CONCEPT_P (atom) = true; |
| |
| if (!info.generate_diagnostics ()) |
| { |
| /* Cache the ATOMIC_CONSTRs that we return, so that sat_hasher::equal |
| later can cheaply compare two atoms using just pointer equality. */ |
| if (!atom_cache) |
| atom_cache = hash_table<atom_hasher>::create_ggc (31); |
| tree *slot = atom_cache->find_slot (atom, INSERT); |
| if (*slot) |
| return *slot; |
| |
| /* Find all template parameters used in the targets of the parameter |
| mapping, and store a list of them in the TREE_TYPE of the mapping. |
| This list will be used by sat_hasher to determine the subset of |
| supplied template arguments that the satisfaction value of the atom |
| depends on. */ |
| if (map) |
| { |
| tree targets = make_tree_vec (list_length (map)); |
| int i = 0; |
| for (tree node = map; node; node = TREE_CHAIN (node)) |
| { |
| tree target = TREE_PURPOSE (node); |
| TREE_VEC_ELT (targets, i++) = target; |
| } |
| tree target_parms = find_template_parameters (targets, |
| info.initial_parms); |
| TREE_TYPE (map) = target_parms; |
| } |
| |
| *slot = atom; |
| } |
| return atom; |
| } |
| |
| /* Returns the normal form of an expression. */ |
| |
| static tree |
| normalize_expression (tree t, tree args, norm_info info) |
| { |
| if (!t) |
| return NULL_TREE; |
| |
| if (t == error_mark_node) |
| return error_mark_node; |
| |
| switch (TREE_CODE (t)) |
| { |
| case TRUTH_ANDIF_EXPR: |
| return normalize_logical_operation (t, args, CONJ_CONSTR, info); |
| case TRUTH_ORIF_EXPR: |
| return normalize_logical_operation (t, args, DISJ_CONSTR, info); |
| default: |
| return normalize_atom (t, args, info); |
| } |
| } |
| |
| /* Cache of the normalized form of constraints. Marked as deletable because it |
| can all be recalculated. */ |
| static GTY((deletable)) hash_map<tree,tree> *normalized_map; |
| |
| static tree |
| get_normalized_constraints (tree t, norm_info info) |
| { |
| auto_timevar time (TV_CONSTRAINT_NORM); |
| return normalize_expression (t, NULL_TREE, info); |
| } |
| |
| /* Returns the normalized constraints from a constraint-info object |
| or NULL_TREE if the constraints are null. IN_DECL provides the |
| declaration to which the constraints belong. */ |
| |
| static tree |
| get_normalized_constraints_from_info (tree ci, tree in_decl, bool diag = false) |
| { |
| if (ci == NULL_TREE) |
| return NULL_TREE; |
| |
| /* Substitution errors during normalization are fatal. */ |
| ++processing_template_decl; |
| norm_info info (in_decl, diag ? tf_norm : tf_none); |
| tree t = get_normalized_constraints (CI_ASSOCIATED_CONSTRAINTS (ci), info); |
| --processing_template_decl; |
| |
| return t; |
| } |
| |
| /* Returns the normalized constraints for the declaration D. */ |
| |
| static tree |
| get_normalized_constraints_from_decl (tree d, bool diag = false) |
| { |
| tree tmpl; |
| tree decl; |
| |
| /* For inherited constructors, consider the original declaration; |
| it has the correct template information attached. */ |
| d = strip_inheriting_ctors (d); |
| |
| if (regenerated_lambda_fn_p (d)) |
| { |
| /* If this lambda was regenerated, DECL_TEMPLATE_PARMS doesn't contain |
| all in-scope template parameters, but the lambda from which it was |
| ultimately regenerated does, so use that instead. */ |
| tree lambda = CLASSTYPE_LAMBDA_EXPR (DECL_CONTEXT (d)); |
| lambda = most_general_lambda (lambda); |
| d = lambda_function (lambda); |
| } |
| |
| if (TREE_CODE (d) == TEMPLATE_DECL) |
| { |
| tmpl = d; |
| decl = DECL_TEMPLATE_RESULT (tmpl); |
| } |
| else |
| { |
| if (tree ti = DECL_TEMPLATE_INFO (d)) |
| tmpl = TI_TEMPLATE (ti); |
| else |
| tmpl = NULL_TREE; |
| decl = d; |
| } |
| |
| /* Get the most general template for the declaration, and compute |
| arguments from that. This ensures that the arguments used for |
| normalization are always template parameters and not arguments |
| used for outer specializations. For example: |
| |
| template<typename T> |
| struct S { |
| template<typename U> requires C<T, U> void f(U); |
| }; |
| |
| S<int>::f(0); |
| |
| When we normalize the requirements for S<int>::f, we want the |
| arguments to be {T, U}, not {int, U}. One reason for this is that |
| accepting the latter causes the template parameter level of U |
| to be reduced in a way that makes it overly difficult substitute |
| concrete arguments (i.e., eventually {int, int} during satisfaction. */ |
| if (tmpl) |
| { |
| if (DECL_LANG_SPECIFIC(tmpl) && !DECL_TEMPLATE_SPECIALIZATION (tmpl)) |
| tmpl = most_general_template (tmpl); |
| } |
| |
| d = tmpl ? tmpl : decl; |
| |
| /* If we're not diagnosing errors, use cached constraints, if any. */ |
| if (!diag) |
| if (tree *p = hash_map_safe_get (normalized_map, d)) |
| return *p; |
| |
| tree norm = NULL_TREE; |
| if (tree ci = get_constraints (d)) |
| { |
| push_access_scope_guard pas (decl); |
| norm = get_normalized_constraints_from_info (ci, tmpl, diag); |
| } |
| |
| if (!diag) |
| hash_map_safe_put<hm_ggc> (normalized_map, d, norm); |
| |
| return norm; |
| } |
| |
| /* Returns the normal form of TMPL's definition. */ |
| |
| static tree |
| normalize_concept_definition (tree tmpl, bool diag = false) |
| { |
| if (!diag) |
| if (tree *p = hash_map_safe_get (normalized_map, tmpl)) |
| return *p; |
| |
| 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; |
| norm_info info (tmpl, diag ? tf_norm : tf_none); |
| tree norm = get_normalized_constraints (def, info); |
| --processing_template_decl; |
| |
| if (!diag) |
| hash_map_safe_put<hm_ggc> (normalized_map, tmpl, norm); |
| |
| return norm; |
| } |
| |
| /* Normalize an EXPR as a constraint. */ |
| |
| static tree |
| normalize_constraint_expression (tree expr, norm_info info) |
| { |
| if (!expr || expr == error_mark_node) |
| return expr; |
| |
| if (!info.generate_diagnostics ()) |
| if (tree *p = hash_map_safe_get (normalized_map, expr)) |
| return *p; |
| |
| ++processing_template_decl; |
| tree norm = get_normalized_constraints (expr, info); |
| --processing_template_decl; |
| |
| if (!info.generate_diagnostics ()) |
| hash_map_safe_put<hm_ggc> (normalized_map, expr, norm); |
| |
| return norm; |
| } |
| |
| /* 17.4.1.2p2. Two constraints are identical if they are formed |
| from the same expression and the targets of the parameter mapping |
| are equivalent. */ |
| |
| bool |
| atomic_constraints_identical_p (tree t1, tree t2) |
| { |
| gcc_assert (TREE_CODE (t1) == ATOMIC_CONSTR); |
| gcc_assert (TREE_CODE (t2) == ATOMIC_CONSTR); |
| |
| if (ATOMIC_CONSTR_EXPR (t1) != ATOMIC_CONSTR_EXPR (t2)) |
| return false; |
| |
| if (!parameter_mapping_equivalent_p (t1, t2)) |
| return false; |
| |
| return true; |
| } |
| |
| /* True if T1 and T2 are equivalent, meaning they have the same syntactic |
| structure and all corresponding constraints are identical. */ |
| |
| bool |
| constraints_equivalent_p (tree t1, tree t2) |
| { |
| gcc_assert (CONSTR_P (t1)); |
| gcc_assert (CONSTR_P (t2)); |
| |
| if (TREE_CODE (t1) != TREE_CODE (t2)) |
| return false; |
| |
| switch (TREE_CODE (t1)) |
| { |
| case CONJ_CONSTR: |
| case DISJ_CONSTR: |
| if (!constraints_equivalent_p (TREE_OPERAND (t1, 0), TREE_OPERAND (t2, 0))) |
| return false; |
| if (!constraints_equivalent_p (TREE_OPERAND (t1, 1), TREE_OPERAND (t2, 1))) |
| return false; |
| break; |
| case ATOMIC_CONSTR: |
| if (!atomic_constraints_identical_p(t1, t2)) |
| return false; |
| break; |
| default: |
| gcc_unreachable (); |
| } |
| return true; |
| } |
| |
| /* Compute the hash value for T. */ |
| |
| hashval_t |
| hash_atomic_constraint (tree t) |
| { |
| gcc_assert (TREE_CODE (t) == ATOMIC_CONSTR); |
| |
| /* Hash the identity of the expression. */ |
| hashval_t val = htab_hash_pointer (ATOMIC_CONSTR_EXPR (t)); |
| |
| /* Hash the targets of the parameter map. */ |
| tree p = ATOMIC_CONSTR_MAP (t); |
| while (p) |
| { |
| val = iterative_hash_template_arg (TREE_PURPOSE (p), val); |
| p = TREE_CHAIN (p); |
| } |
| |
| return val; |
| } |
| |
| namespace inchash |
| { |
| |
| static void |
| add_constraint (tree t, hash& h) |
| { |
| h.add_int(TREE_CODE (t)); |
| switch (TREE_CODE (t)) |
| { |
| case CONJ_CONSTR: |
| case DISJ_CONSTR: |
| add_constraint (TREE_OPERAND (t, 0), h); |
| add_constraint (TREE_OPERAND (t, 1), h); |
| break; |
| case ATOMIC_CONSTR: |
| h.merge_hash (hash_atomic_constraint (t)); |
| break; |
| default: |
| gcc_unreachable (); |
| } |
| } |
| |
| } |
| |
| /* Computes a hash code for the constraint T. */ |
| |
| hashval_t |
| iterative_hash_constraint (tree t, hashval_t val) |
| { |
| gcc_assert (CONSTR_P (t)); |
| inchash::hash h (val); |
| inchash::add_constraint (t, h); |
| return h.end (); |
| } |
| |
| // -------------------------------------------------------------------------- // |
| // Constraint Semantic Processing |
| // |
| // The following functions are called by the parser and substitution rules |
| // to create and evaluate constraint-related nodes. |
| |
| // The constraints associated with the current template parameters. |
| tree |
| current_template_constraints (void) |
| { |
| if (!current_template_parms) |
| return NULL_TREE; |
| tree tmpl_constr = TEMPLATE_PARMS_CONSTRAINTS (current_template_parms); |
| return build_constraints (tmpl_constr, NULL_TREE); |
| } |
| |
| /* If the recently parsed TYPE declares or defines a template or |
| template specialization, get its corresponding constraints from the |
| current template parameters and bind them to TYPE's declaration. */ |
| |
| tree |
| associate_classtype_constraints (tree type) |
| { |
| if (!type || type == error_mark_node || !CLASS_TYPE_P (type)) |
| return type; |
| |
| /* An explicit class template specialization has no template parameters. */ |
| if (!current_template_parms) |
| return type; |
| |
| if (CLASSTYPE_IS_TEMPLATE (type) || CLASSTYPE_TEMPLATE_SPECIALIZATION (type)) |
| { |
| tree decl = TYPE_STUB_DECL (type); |
| tree ci = current_template_constraints (); |
| |
| /* An implicitly instantiated member template declaration already |
| has associated constraints. If it is defined outside of its |
| class, then we need match these constraints against those of |
| original declaration. */ |
| if (tree orig_ci = get_constraints (decl)) |
| { |
| if (int extra_levels = (TMPL_PARMS_DEPTH (current_template_parms) |
| - TMPL_ARGS_DEPTH (TYPE_TI_ARGS (type)))) |
| { |
| /* If there is a discrepancy between the current template depth |
| and the template depth of the original declaration, then we |
| must be redeclaring a class template as part of a friend |
| declaration within another class template. Before matching |
| constraints, we need to reduce the template parameter level |
| within the current constraints via substitution. */ |
| tree outer_gtargs = template_parms_to_args (current_template_parms); |
| TREE_VEC_LENGTH (outer_gtargs) = extra_levels; |
| ci = tsubst_constraint_info (ci, outer_gtargs, tf_none, NULL_TREE); |
| } |
| if (!equivalent_constraints (ci, orig_ci)) |
| { |
| error ("%qT does not match original declaration", type); |
| tree tmpl = CLASSTYPE_TI_TEMPLATE (type); |
| location_t loc = DECL_SOURCE_LOCATION (tmpl); |
| inform (loc, "original template declaration here"); |
| /* Fall through, so that we define the type anyway. */ |
| } |
| return type; |
| } |
| set_constraints (decl, ci); |
| } |
| return type; |
| } |
| |
| /* Create an empty constraint info block. */ |
| |
| static inline tree_constraint_info* |
| build_constraint_info () |
| { |
| return (tree_constraint_info *)make_node (CONSTRAINT_INFO); |
| } |
| |
| /* Build a constraint-info object that contains the associated constraints |
| of a declaration. This also includes the declaration's template |
| requirements (TREQS) and any trailing requirements for a function |
| declarator (DREQS). Note that both TREQS and DREQS must be constraints. |
| |
| If the declaration has neither template nor declaration requirements |
| this returns NULL_TREE, indicating an unconstrained declaration. */ |
| |
| tree |
| build_constraints (tree tr, tree dr) |
| { |
| if (!tr && !dr) |
| return NULL_TREE; |
| |
| tree_constraint_info* ci = build_constraint_info (); |
| ci->template_reqs = tr; |
| ci->declarator_reqs = dr; |
| ci->associated_constr = combine_constraint_expressions (tr, dr); |
| |
| return (tree)ci; |
| } |
| |
| /* Add constraint RHS to the end of CONSTRAINT_INFO ci. */ |
| |
| tree |
| append_constraint (tree ci, tree rhs) |
| { |
| tree tr = ci ? CI_TEMPLATE_REQS (ci) : NULL_TREE; |
| tree dr = ci ? CI_DECLARATOR_REQS (ci) : NULL_TREE; |
| dr = combine_constraint_expressions (dr, rhs); |
| if (ci) |
| { |
| CI_DECLARATOR_REQS (ci) = dr; |
| tree ac = combine_constraint_expressions (tr, dr); |
| CI_ASSOCIATED_CONSTRAINTS (ci) = ac; |
| } |
| else |
| ci = build_constraints (tr, dr); |
| return ci; |
| } |
| |
| /* A mapping from declarations to constraint information. */ |
| |
| static GTY ((cache)) decl_tree_cache_map *decl_constraints; |
| |
| /* Returns the template constraints of declaration T. If T is not |
| constrained, return NULL_TREE. Note that T must be non-null. */ |
| |
| tree |
| get_constraints (const_tree t) |
| { |
| if (!flag_concepts) |
| return NULL_TREE; |
| if (!decl_constraints) |
| return NULL_TREE; |
| |
| gcc_assert (DECL_P (t)); |
| if (TREE_CODE (t) == TEMPLATE_DECL) |
| t = DECL_TEMPLATE_RESULT (t); |
| tree* found = decl_constraints->get (CONST_CAST_TREE (t)); |
| if (found) |
| return *found; |
| else |
| return NULL_TREE; |
| } |
| |
| /* Associate the given constraint information CI with the declaration |
| T. If T is a template, then the constraints are associated with |
| its underlying declaration. Don't build associations if CI is |
| NULL_TREE. */ |
| |
| void |
| set_constraints (tree t, tree ci) |
| { |
| if (!ci) |
| return; |
| gcc_assert (t && flag_concepts); |
| if (TREE_CODE (t) == TEMPLATE_DECL) |
| t = DECL_TEMPLATE_RESULT (t); |
| bool found = hash_map_safe_put<hm_ggc> (decl_constraints, t, ci); |
| gcc_assert (!found); |
| } |
| |
| /* Remove the associated constraints of the declaration T. */ |
| |
| void |
| remove_constraints (tree t) |
| { |
| gcc_checking_assert (DECL_P (t)); |
| if (TREE_CODE (t) == TEMPLATE_DECL) |
| t = DECL_TEMPLATE_RESULT (t); |
| |
| if (decl_constraints) |
| decl_constraints->remove (t); |
| } |
| |
| /* If DECL is a friend, substitute into REQS to produce requirements suitable |
| for declaration matching. */ |
| |
| tree |
| maybe_substitute_reqs_for (tree reqs, const_tree decl) |
| { |
| if (reqs == NULL_TREE) |
| return NULL_TREE; |
| |
| decl = STRIP_TEMPLATE (decl); |
| if (DECL_UNIQUE_FRIEND_P (decl) && DECL_TEMPLATE_INFO (decl)) |
| { |
| tree tmpl = DECL_TI_TEMPLATE (decl); |
| tree gargs = generic_targs_for (tmpl); |
| processing_template_decl_sentinel s; |
| if (uses_template_parms (gargs)) |
| ++processing_template_decl; |
| reqs = tsubst_constraint (reqs, gargs, |
| tf_warning_or_error, NULL_TREE); |
| } |
| return reqs; |
| } |
| |
| /* Returns the trailing requires clause of the declarator of |
| a template declaration T or NULL_TREE if none. */ |
| |
| tree |
| get_trailing_function_requirements (tree t) |
| { |
| tree ci = get_constraints (t); |
| if (!ci) |
| return NULL_TREE; |
| return CI_DECLARATOR_REQS (ci); |
| } |
| |
| /* Construct a sequence of template arguments by prepending |
| ARG to REST. Either ARG or REST may be null. */ |
| static tree |
| build_concept_check_arguments (tree arg, tree rest) |
| { |
| gcc_assert (rest ? TREE_CODE (rest) == TREE_VEC : true); |
| tree args; |
| if (arg) |
| { |
| int n = rest ? TREE_VEC_LENGTH (rest) : 0; |
| args = make_tree_vec (n + 1); |
| TREE_VEC_ELT (args, 0) = arg; |
| if (rest) |
| for (int i = 0; i < n; ++i) |
| TREE_VEC_ELT (args, i + 1) = TREE_VEC_ELT (rest, i); |
| int def = rest ? GET_NON_DEFAULT_TEMPLATE_ARGS_COUNT (rest) : 0; |
| SET_NON_DEFAULT_TEMPLATE_ARGS_COUNT (args, def + 1); |
| } |
| else |
| { |
| args = rest; |
| } |
| return args; |
| } |
| |
| /* Builds an id-expression of the form `C<Args...>()` where C is a function |
| concept. */ |
| |
| static tree |
| build_function_check (tree tmpl, tree args, tsubst_flags_t /*complain*/) |
| { |
| if (TREE_CODE (tmpl) == TEMPLATE_DECL) |
| { |
| /* If we just got a template, wrap it in an overload so it looks like any |
| other template-id. */ |
| tmpl = ovl_make (tmpl); |
| TREE_TYPE (tmpl) = boolean_type_node; |
| } |
| |
| /* Perform function concept resolution now so we always have a single |
| function of the overload set (even if we started with only one; the |
| resolution function converts template arguments). Note that we still |
| wrap this in an overload set so we don't upset other parts of the |
| compiler that expect template-ids referring to function concepts |
| to have an overload set. */ |
| tree info = resolve_function_concept_overload (tmpl, args); |
| if (info == error_mark_node) |
| return error_mark_node; |
| if (!info) |
| { |
| error ("no matching concepts for %qE", tmpl); |
| return error_mark_node; |
| } |
| args = TREE_PURPOSE (info); |
| tmpl = DECL_TI_TEMPLATE (TREE_VALUE (info)); |
| |
| /* Rebuild the singleton overload set; mark the type bool. */ |
| tmpl = ovl_make (tmpl, NULL_TREE); |
| TREE_TYPE (tmpl) = boolean_type_node; |
| |
| /* Build the id-expression around the overload set. */ |
| tree id = build2 (TEMPLATE_ID_EXPR, boolean_type_node, tmpl, args); |
| |
| /* Finally, build the call expression around the overload. */ |
| ++processing_template_decl; |
| vec<tree, va_gc> *fargs = make_tree_vector (); |
| tree call = build_min_nt_call_vec (id, fargs); |
| TREE_TYPE (call) = boolean_type_node; |
| release_tree_vector (fargs); |
| --processing_template_decl; |
| |
| return call; |
| } |
| |
| /* Builds an id-expression of the form `C<Args...>` where C is a variable |
| concept. */ |
| |
| static tree |
| build_variable_check (tree tmpl, tree args, tsubst_flags_t complain) |
| { |
| gcc_assert (variable_concept_p (tmpl)); |
| gcc_assert (TREE_CODE (tmpl) == TEMPLATE_DECL); |
| tree parms = INNERMOST_TEMPLATE_PARMS (DECL_TEMPLATE_PARMS (tmpl)); |
| args = coerce_template_parms (parms, args, tmpl, complain); |
| if (args == error_mark_node) |
| return error_mark_node; |
| return build2 (TEMPLATE_ID_EXPR, boolean_type_node, tmpl, args); |
| } |
| |
| /* Builds an id-expression of the form `C<Args...>` where C is a standard |
| concept. */ |
| |
| static tree |
| build_standard_check (tree tmpl, tree args, tsubst_flags_t complain) |
| { |
| gcc_assert (standard_concept_p (tmpl)); |
| gcc_assert (TREE_CODE (tmpl) == TEMPLATE_DECL); |
| tree parms = INNERMOST_TEMPLATE_PARMS (DECL_TEMPLATE_PARMS (tmpl)); |
| args = coerce_template_parms (parms, args, tmpl, complain); |
| if (args == error_mark_node) |
| return error_mark_node; |
| return build2 (TEMPLATE_ID_EXPR, boolean_type_node, tmpl, args); |
| } |
| |
| /* Construct an expression that checks TARGET using ARGS. */ |
| |
| tree |
| build_concept_check (tree target, tree args, tsubst_flags_t complain) |
| { |
| return build_concept_check (target, NULL_TREE, args, complain); |
| } |
| |
| /* Construct an expression that checks the concept given by DECL. If |
| concept_definition_p (DECL) is false, this returns null. */ |
| |
| tree |
| build_concept_check (tree decl, tree arg, tree rest, tsubst_flags_t complain) |
| { |
| tree args = build_concept_check_arguments (arg, rest); |
| |
| if (standard_concept_p (decl)) |
| return build_standard_check (decl, args, complain); |
| if (variable_concept_p (decl)) |
| return build_variable_check (decl, args, complain); |
| if (function_concept_p (decl)) |
| return build_function_check (decl, args, complain); |
| |
| return error_mark_node; |
| } |
| |
| /* Build a template-id that can participate in a concept check. */ |
| |
| static tree |
| build_concept_id (tree decl, tree args) |
| { |
| tree check = build_concept_check (decl, args, tf_warning_or_error); |
| if (check == error_mark_node) |
| return error_mark_node; |
| return unpack_concept_check (check); |
| } |
| |
| /* Build a template-id that can participate in a concept check, preserving |
| the source location of the original template-id. */ |
| |
| tree |
| build_concept_id (tree expr) |
| { |
| gcc_assert (TREE_CODE (expr) == TEMPLATE_ID_EXPR); |
| tree id = build_concept_id (TREE_OPERAND (expr, 0), TREE_OPERAND (expr, 1)); |
| protected_set_expr_location (id, cp_expr_location (expr)); |
| return id; |
| } |
| |
| /* Build as template-id with a placeholder that can be used as a |
| type constraint. |
| |
| Note that this will diagnose errors if the initial concept check |
| cannot be built. */ |
| |
| tree |
| build_type_constraint (tree decl, tree args, tsubst_flags_t complain) |
| { |
| tree wildcard = build_nt (WILDCARD_DECL); |
| ++processing_template_decl; |
| tree check = build_concept_check (decl, wildcard, args, complain); |
| --processing_template_decl; |
| if (check == error_mark_node) |
| return error_mark_node; |
| return unpack_concept_check (check); |
| } |
| |
| /* Returns a TYPE_DECL that contains sufficient information to |
| build a template parameter of the same kind as PROTO and |
| constrained by the concept declaration CNC. Note that PROTO |
| is the first template parameter of CNC. |
| |
| If specified, ARGS provides additional arguments to the |
| constraint check. */ |
| tree |
| build_constrained_parameter (tree cnc, tree proto, tree args) |
| { |
| tree name = DECL_NAME (cnc); |
| tree type = TREE_TYPE (proto); |
| tree decl = build_decl (input_location, TYPE_DECL, name, type); |
| CONSTRAINED_PARM_PROTOTYPE (decl) = proto; |
| CONSTRAINED_PARM_CONCEPT (decl) = cnc; |
| CONSTRAINED_PARM_EXTRA_ARGS (decl) = args; |
| return decl; |
| } |
| |
| /* Create a constraint expression for the given DECL that evaluates the |
| requirements specified by CONSTR, a TYPE_DECL that contains all the |
| information necessary to build the requirements (see finish_concept_name |
| for the layout of that TYPE_DECL). |
| |
| Note that the constraints are neither reduced nor decomposed. That is |
| done only after the requires clause has been parsed (or not). */ |
| |
| tree |
| finish_shorthand_constraint (tree decl, tree constr) |
| { |
| /* No requirements means no constraints. */ |
| if (!constr) |
| return NULL_TREE; |
| |
| if (error_operand_p (constr)) |
| return NULL_TREE; |
| |
| tree proto = CONSTRAINED_PARM_PROTOTYPE (constr); |
| tree con = CONSTRAINED_PARM_CONCEPT (constr); |
| tree args = CONSTRAINED_PARM_EXTRA_ARGS (constr); |
| |
| /* The TS lets use shorthand to constrain a pack of arguments, but the |
| standard does not. |
| |
| For the TS, consider: |
| |
| template<C... Ts> struct s; |
| |
| If C is variadic (and because Ts is a pack), we associate the |
| constraint C<Ts...>. In all other cases, we associate |
| the constraint (C<Ts> && ...). |
| |
| The standard behavior cannot be overridden by -fconcepts-ts. */ |
| bool variadic_concept_p = template_parameter_pack_p (proto); |
| bool declared_pack_p = template_parameter_pack_p (decl); |
| bool apply_to_each_p = (cxx_dialect >= cxx20) ? true : !variadic_concept_p; |
| |
| /* Get the argument and overload used for the requirement |
| and adjust it if we're going to expand later. */ |
| tree arg = template_parm_to_arg (decl); |
| if (apply_to_each_p && declared_pack_p) |
| arg = PACK_EXPANSION_PATTERN (TREE_VEC_ELT (ARGUMENT_PACK_ARGS (arg), 0)); |
| |
| /* Build the concept constraint-expression. */ |
| tree tmpl = DECL_TI_TEMPLATE (con); |
| tree check = tmpl; |
| if (TREE_CODE (con) == FUNCTION_DECL) |
| check = ovl_make (tmpl); |
| check = build_concept_check (check, arg, args, tf_warning_or_error); |
| |
| /* Make the check a fold-expression if needed. */ |
| if (apply_to_each_p && declared_pack_p) |
| check = finish_left_unary_fold_expr (check, TRUTH_ANDIF_EXPR); |
| |
| return check; |
| } |
| |
| /* Returns a conjunction of shorthand requirements for the template |
| parameter list PARMS. Note that the requirements are stored in |
| the TYPE of each tree node. */ |
| |
| tree |
| get_shorthand_constraints (tree parms) |
| { |
| tree result = NULL_TREE; |
| parms = INNERMOST_TEMPLATE_PARMS (parms); |
| for (int i = 0; i < TREE_VEC_LENGTH (parms); ++i) |
| { |
| tree parm = TREE_VEC_ELT (parms, i); |
| tree constr = TEMPLATE_PARM_CONSTRAINTS (parm); |
| result = combine_constraint_expressions (result, constr); |
| } |
| return result; |
| } |
| |
| /* Get the deduced wildcard from a DEDUCED placeholder. If the deduced |
| wildcard is a pack, return the first argument of that pack. */ |
| |
| static tree |
| get_deduced_wildcard (tree wildcard) |
| { |
| if (ARGUMENT_PACK_P (wildcard)) |
| wildcard = TREE_VEC_ELT (ARGUMENT_PACK_ARGS (wildcard), 0); |
| gcc_assert (TREE_CODE (wildcard) == WILDCARD_DECL); |
| return wildcard; |
| } |
| |
| /* Returns the prototype parameter for the nth deduced wildcard. */ |
| |
| static tree |
| get_introduction_prototype (tree wildcards, int index) |
| { |
| return TREE_TYPE (get_deduced_wildcard (TREE_VEC_ELT (wildcards, index))); |
| } |
| |
| /* Introduce a type template parameter. */ |
| |
| static tree |
| introduce_type_template_parameter (tree wildcard, bool& non_type_p) |
| { |
| non_type_p = false; |
| return finish_template_type_parm (class_type_node, DECL_NAME (wildcard)); |
| } |
| |
| /* Introduce a template template parameter. */ |
| |
| static tree |
| introduce_template_template_parameter (tree wildcard, bool& non_type_p) |
| { |
| non_type_p = false; |
| begin_template_parm_list (); |
| current_template_parms = DECL_TEMPLATE_PARMS (TREE_TYPE (wildcard)); |
| end_template_parm_list (); |
| return finish_template_template_parm (class_type_node, DECL_NAME (wildcard)); |
| } |
| |
| /* Introduce a template non-type parameter. */ |
| |
| static tree |
| introduce_nontype_template_parameter (tree wildcard, bool& non_type_p) |
| { |
| non_type_p = true; |
| tree parm = copy_decl (TREE_TYPE (wildcard)); |
| DECL_NAME (parm) = DECL_NAME (wildcard); |
| return parm; |
| } |
| |
| /* Introduce a single template parameter. */ |
| |
| static tree |
| build_introduced_template_parameter (tree wildcard, bool& non_type_p) |
| { |
| tree proto = TREE_TYPE (wildcard); |
| |
| tree parm; |
| if (TREE_CODE (proto) == TYPE_DECL) |
| parm = introduce_type_template_parameter (wildcard, non_type_p); |
| else if (TREE_CODE (proto) == TEMPLATE_DECL) |
| parm = introduce_template_template_parameter (wildcard, non_type_p); |
| else |
| parm = introduce_nontype_template_parameter (wildcard, non_type_p); |
| |
| /* Wrap in a TREE_LIST for process_template_parm. Note that introduced |
| parameters do not retain the defaults from the source parameter. */ |
| return build_tree_list (NULL_TREE, parm); |
| } |
| |
| /* Introduce a single template parameter. */ |
| |
| static tree |
| introduce_template_parameter (tree parms, tree wildcard) |
| { |
| gcc_assert (!ARGUMENT_PACK_P (wildcard)); |
| tree proto = TREE_TYPE (wildcard); |
| location_t loc = DECL_SOURCE_LOCATION (wildcard); |
| |
| /* Diagnose the case where we have C{...Args}. */ |
| if (WILDCARD_PACK_P (wildcard)) |
| { |
| tree id = DECL_NAME (wildcard); |
| error_at (loc, "%qE cannot be introduced with an ellipsis %<...%>", id); |
| inform (DECL_SOURCE_LOCATION (proto), "prototype declared here"); |
| } |
| |
| bool non_type_p; |
| tree parm = build_introduced_template_parameter (wildcard, non_type_p); |
| return process_template_parm (parms, loc, parm, non_type_p, false); |
| } |
| |
| /* Introduce a template parameter pack. */ |
| |
| static tree |
| introduce_template_parameter_pack (tree parms, tree wildcard) |
| { |
| bool non_type_p; |
| tree parm = build_introduced_template_parameter (wildcard, non_type_p); |
| location_t loc = DECL_SOURCE_LOCATION (wildcard); |
| return process_template_parm (parms, loc, parm, non_type_p, true); |
| } |
| |
| /* Introduce the nth template parameter. */ |
| |
| static tree |
| introduce_template_parameter (tree parms, tree wildcards, int& index) |
| { |
| tree deduced = TREE_VEC_ELT (wildcards, index++); |
| return introduce_template_parameter (parms, deduced); |
| } |
| |
| /* Introduce either a template parameter pack or a list of template |
| parameters. */ |
| |
| static tree |
| introduce_template_parameters (tree parms, tree wildcards, int& index) |
| { |
| /* If the prototype was a parameter, we better have deduced an |
| argument pack, and that argument must be the last deduced value |
| in the wildcard vector. */ |
| tree deduced = TREE_VEC_ELT (wildcards, index++); |
| gcc_assert (ARGUMENT_PACK_P (deduced)); |
| gcc_assert (index == TREE_VEC_LENGTH (wildcards)); |
| |
| /* Introduce each element in the pack. */ |
| tree args = ARGUMENT_PACK_ARGS (deduced); |
| for (int i = 0; i < TREE_VEC_LENGTH (args); ++i) |
| { |
| tree arg = TREE_VEC_ELT (args, i); |
| if (WILDCARD_PACK_P (arg)) |
| parms = introduce_template_parameter_pack (parms, arg); |
| else |
| parms = introduce_template_parameter (parms, arg); |
| } |
| |
| return parms; |
| } |
| |
| /* Builds the template parameter list PARMS by chaining introduced |
| parameters from the WILDCARD vector. INDEX is the position of |
| the current parameter. */ |
| |
| static tree |
| process_introduction_parms (tree parms, tree wildcards, int& index) |
| { |
| tree proto = get_introduction_prototype (wildcards, index); |
| if (template_parameter_pack_p (proto)) |
| return introduce_template_parameters (parms, wildcards, index); |
| else |
| return introduce_template_parameter (parms, wildcards, index); |
| } |
| |
| /* Ensure that all template parameters have been introduced for the concept |
| named in CHECK. If not, emit a diagnostic. |
| |
| Note that implicitly introducing a parameter with a default argument |
| creates a case where a parameter is declared, but unnamed, making |
| it unusable in the definition. */ |
| |
| static bool |
| check_introduction_list (tree intros, tree check) |
| { |
| check = unpack_concept_check (check); |
| tree tmpl = TREE_OPERAND (check, 0); |
| if (OVL_P (tmpl)) |
| tmpl = OVL_FIRST (tmpl); |
| |
| tree parms = DECL_INNERMOST_TEMPLATE_PARMS (tmpl); |
| if (TREE_VEC_LENGTH (intros) < TREE_VEC_LENGTH (parms)) |
| { |
| error_at (input_location, "all template parameters of %qD must " |
| "be introduced", tmpl); |
| return false; |
| } |
| |
| return true; |
| } |
| |
| /* Associates a constraint check to the current template based on the |
| introduction parameters. INTRO_LIST must be a TREE_VEC of WILDCARD_DECLs |
| containing a chained PARM_DECL which contains the identifier as well as |
| the source location. TMPL_DECL is the decl for the concept being used. |
| If we take a concept, C, this will form a check in the form of |
| C<INTRO_LIST> filling in any extra arguments needed by the defaults |
| deduced. |
| |
| Returns NULL_TREE if no concept could be matched and error_mark_node if |
| an error occurred when matching. */ |
| |
| tree |
| finish_template_introduction (tree tmpl_decl, |
| tree intro_list, |
| location_t intro_loc) |
| { |
| /* Build a concept check to deduce the actual parameters. */ |
| tree expr = build_concept_check (tmpl_decl, intro_list, tf_none); |
| if (expr == error_mark_node) |
| { |
| error_at (intro_loc, "cannot deduce template parameters from " |
| "introduction list"); |
| return error_mark_node; |
| } |
| |
| if (!check_introduction_list (intro_list, expr)) |
| return error_mark_node; |
| |
| tree parms = deduce_concept_introduction (expr); |
| if (!parms) |
| return NULL_TREE; |
| |
| /* Build template parameter scope for introduction. */ |
| tree parm_list = NULL_TREE; |
| begin_template_parm_list (); |
| int nargs = MIN (TREE_VEC_LENGTH (parms), TREE_VEC_LENGTH (intro_list)); |
| for (int n = 0; n < nargs; ) |
| parm_list = process_introduction_parms (parm_list, parms, n); |
| parm_list = end_template_parm_list (parm_list); |
| |
| /* Update the number of arguments to reflect the number of deduced |
| template parameter introductions. */ |
| nargs = TREE_VEC_LENGTH (parm_list); |
| |
| /* Determine if any errors occurred during matching. */ |
| for (int i = 0; i < TREE_VEC_LENGTH (parm_list); ++i) |
| if (TREE_VALUE (TREE_VEC_ELT (parm_list, i)) == error_mark_node) |
| { |
| end_template_decl (); |
| return error_mark_node; |
| } |
| |
| /* Build a concept check for our constraint. */ |
| tree check_args = make_tree_vec (nargs); |
| int n = 0; |
| for (; n < TREE_VEC_LENGTH (parm_list); ++n) |
| { |
| tree parm = TREE_VEC_ELT (parm_list, n); |
| TREE_VEC_ELT (check_args, n) = template_parm_to_arg (parm); |
| } |
| SET_NON_DEFAULT_TEMPLATE_ARGS_COUNT (check_args, n); |
| |
| /* If the template expects more parameters we should be able |
| to use the defaults from our deduced concept. */ |
| for (; n < TREE_VEC_LENGTH (parms); ++n) |
| TREE_VEC_ELT (check_args, n) = TREE_VEC_ELT (parms, n); |
| |
| /* Associate the constraint. */ |
| tree check = build_concept_check (tmpl_decl, |
| check_args, |
| tf_warning_or_error); |
| TEMPLATE_PARMS_CONSTRAINTS (current_template_parms) = check; |
| |
| return parm_list; |
| } |
| |
| |
| /* Given the concept check T from a constrained-type-specifier, extract |
| its TMPL and ARGS. FIXME why do we need two different forms of |
| constrained-type-specifier? */ |
| |
| void |
| placeholder_extract_concept_and_args (tree t, tree &tmpl, tree &args) |
| { |
| if (concept_check_p (t)) |
| { |
| t = unpack_concept_check (t); |
| tmpl = TREE_OPERAND (t, 0); |
| if (TREE_CODE (tmpl) == OVERLOAD) |
| tmpl = OVL_FIRST (tmpl); |
| args = TREE_OPERAND (t, 1); |
| return; |
| } |
| |
| if (TREE_CODE (t) == TYPE_DECL) |
| { |
| /* A constrained parameter. Build a constraint check |
| based on the prototype parameter and then extract the |
| arguments from that. */ |
| tree proto = CONSTRAINED_PARM_PROTOTYPE (t); |
| tree check = finish_shorthand_constraint (proto, t); |
| placeholder_extract_concept_and_args (check, tmpl, args); |
| return; |
| } |
| } |
| |
| /* Returns true iff the placeholders C1 and C2 are equivalent. C1 |
| and C2 can be either TEMPLATE_TYPE_PARM or template-ids. */ |
| |
| bool |
| equivalent_placeholder_constraints (tree c1, tree c2) |
| { |
| if (c1 && TREE_CODE (c1) == TEMPLATE_TYPE_PARM) |
| /* A constrained auto. */ |
| c1 = PLACEHOLDER_TYPE_CONSTRAINTS (c1); |
| if (c2 && TREE_CODE (c2) == TEMPLATE_TYPE_PARM) |
| c2 = PLACEHOLDER_TYPE_CONSTRAINTS (c2); |
| |
| if (c1 == c2) |
| return true; |
| if (!c1 || !c2) |
| return false; |
| if (c1 == error_mark_node || c2 == error_mark_node) |
| /* We get here during satisfaction; when a deduction constraint |
| fails, substitution can produce an error_mark_node for the |
| placeholder constraints. */ |
| return false; |
| |
| tree t1, t2, a1, a2; |
| placeholder_extract_concept_and_args (c1, t1, a1); |
| placeholder_extract_concept_and_args (c2, t2, a2); |
| |
| if (t1 != t2) |
| return false; |
| |
| int len1 = TREE_VEC_LENGTH (a1); |
| int len2 = TREE_VEC_LENGTH (a2); |
| if (len1 != len2) |
| return false; |
| |
| /* Skip the first argument so we don't infinitely recurse. |
| Also, they may differ in template parameter index. */ |
| for (int i = 1; i < len1; ++i) |
| { |
| tree t1 = TREE_VEC_ELT (a1, i); |
| tree t2 = TREE_VEC_ELT (a2, i); |
| if (!template_args_equal (t1, t2)) |
| return false; |
| } |
| return true; |
| } |
| |
| /* Return a hash value for the placeholder ATOMIC_CONSTR C. */ |
| |
| hashval_t |
| hash_placeholder_constraint (tree c) |
| { |
| tree t, a; |
| placeholder_extract_concept_and_args (c, t, a); |
| |
| /* Like hash_tmpl_and_args, but skip the first argument. */ |
| hashval_t val = iterative_hash_object (DECL_UID (t), 0); |
| |
| for (int i = TREE_VEC_LENGTH (a)-1; i > 0; --i) |
| val = iterative_hash_template_arg (TREE_VEC_ELT (a, i), val); |
| |
| return val; |
| } |
| |
| /* Substitute through the expression of a simple requirement or |
| compound requirement. */ |
| |
| 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); |
| if (convert_to_void (r, ICV_STATEMENT, tf_none) != error_mark_node) |
| return r; |
| |
| if (info.diagnose_unsatisfaction_p ()) |
| { |
| location_t loc = cp_expr_loc_or_input_loc (t); |
| if (diagnosing_failed_constraint::replay_errors_p ()) |
| { |
| 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); |
| else |
| convert_to_void (r, ICV_STATEMENT, info.complain); |
| } |
| else |
| inform (loc, "the required expression %qE is invalid", t); |
| } |
| else if (info.noisy ()) |
| { |
| r = tsubst_expr (t, args, info.complain, info.in_decl, false); |
| convert_to_void (r, ICV_STATEMENT, info.complain); |
| } |
| |
| return error_mark_node; |
| } |
| |
| |
| /* Substitute through the simple requirement. */ |
| |
| static tree |
| tsubst_simple_requirement (tree t, tree args, sat_info info) |
| { |
| tree t0 = TREE_OPERAND (t, 0); |
| tree expr = tsubst_valid_expression_requirement (t0, args, info); |
| if (expr == error_mark_node) |
| return error_mark_node; |
| return boolean_true_node; |
| } |
| |
| /* Subroutine of tsubst_type_requirement that performs the actual substitution |
| and diagnosing. Also used by tsubst_compound_requirement. */ |
| |
| static tree |
| tsubst_type_requirement_1 (tree t, tree args, sat_info info, location_t loc) |
| { |
| tree r = tsubst (t, args, tf_none, info.in_decl); |
| if (r != error_mark_node) |
| return r; |
| |
| if (info.diagnose_unsatisfaction_p ()) |
| { |
| if (diagnosing_failed_constraint::replay_errors_p ()) |
| { |
| /* Replay the substitution error. */ |
| inform (loc, "the required type %qT is invalid, because", t); |
| tsubst (t, args, info.complain, info.in_decl); |
| } |
| else |
| inform (loc, "the required type %qT is invalid", t); |
| } |
| else if (info.noisy ()) |
| tsubst (t, args, info.complain, info.in_decl); |
| |
| return error_mark_node; |
| } |
| |
| |
| /* Substitute through the type requirement. */ |
| |
| static tree |
| tsubst_type_requirement (tree t, tree args, sat_info info) |
| { |
| tree t0 = TREE_OPERAND (t, 0); |
| tree type = tsubst_type_requirement_1 (t0, args, info, EXPR_LOCATION (t)); |
| if (type == error_mark_node) |
| return error_mark_node; |
| return boolean_true_node; |
| } |
| |
| /* True if TYPE can be deduced from EXPR. */ |
| |
| static bool |
| type_deducible_p (tree expr, tree type, tree placeholder, tree args, |
| subst_info info) |
| { |
| /* Make sure deduction is performed against ( EXPR ), so that |
| references are preserved in the result. */ |
| expr = force_paren_expr_uneval (expr); |
| |
| tree deduced_type = do_auto_deduction (type, expr, placeholder, |
| info.complain, adc_requirement, |
| /*outer_targs=*/args); |
| |
| return deduced_type != error_mark_node; |
| } |
| |
| /* True if EXPR can not be converted to TYPE. */ |
| |
| static bool |
| expression_convertible_p (tree expr, tree type, subst_info info) |
| { |
| tree conv = |
| perform_direct_initialization_if_possible (type, expr, false, |
| info.complain); |
| if (conv == error_mark_node) |
| return false; |
| if (conv == NULL_TREE) |
| { |
| if (info.complain & tf_error) |
| { |
| location_t loc = EXPR_LOC_OR_LOC (expr, input_location); |
| error_at (loc, "cannot convert %qE to %qT", expr, type); |
| } |
| return false; |
| } |
| return true; |
| } |
| |
| |
| /* Substitute through the compound requirement. */ |
| |
| static tree |
| tsubst_compound_requirement (tree t, tree args, sat_info info) |
| { |
| tree t0 = TREE_OPERAND (t, 0); |
| tree t1 = TREE_OPERAND (t, 1); |
| tree expr = tsubst_valid_expression_requirement (t0, args, info); |
| if (expr == error_mark_node) |
| return error_mark_node; |
| |
| location_t loc = cp_expr_loc_or_input_loc (expr); |
| |
| /* Check the noexcept condition. */ |
| bool noexcept_p = COMPOUND_REQ_NOEXCEPT_P (t); |
| if (noexcept_p && !expr_noexcept_p (expr, tf_none)) |
| { |
| if (info.diagnose_unsatisfaction_p ()) |
| inform (loc, "%qE is not %<noexcept%>", expr); |
| else |
| return error_mark_node; |
| } |
| |
| /* Substitute through the type expression, if any. */ |
| tree type = tsubst_type_requirement_1 (t1, args, info, EXPR_LOCATION (t)); |
| if (type == error_mark_node) |
| return error_mark_node; |
| |
| subst_info quiet (tf_none, info.in_decl); |
| |
| /* Check expression against the result type. */ |
| if (type) |
| { |
| if (tree placeholder = type_uses_auto (type)) |
| { |
| if (!type_deducible_p (expr, type, placeholder, args, quiet)) |
| { |
| if (info.diagnose_unsatisfaction_p ()) |
| { |
| if (diagnosing_failed_constraint::replay_errors_p ()) |
| { |
| inform (loc, |
| "%qE does not satisfy return-type-requirement, " |
| "because", t0); |
| /* Further explain the reason for the error. */ |
| type_deducible_p (expr, type, placeholder, args, info); |
| } |
| else |
| inform (loc, |
| "%qE does not satisfy return-type-requirement", t0); |
| } |
| return error_mark_node; |
| } |
| } |
| else if (!expression_convertible_p (expr, type, quiet)) |
| { |
| if (info.diagnose_unsatisfaction_p ()) |
| { |
| if (diagnosing_failed_constraint::replay_errors_p ()) |
| { |
| inform (loc, "cannot convert %qE to %qT because", t0, type); |
| /* Further explain the reason for the error. */ |
| expression_convertible_p (expr, type, info); |
| } |
| else |
| inform (loc, "cannot convert %qE to %qT", t0, type); |
| } |
| return error_mark_node; |
| } |
| } |
| |
| return boolean_true_node; |
| } |
| |
| /* Substitute through the nested requirement. */ |
| |
| static tree |
| tsubst_nested_requirement (tree t, tree args, sat_info info) |
| { |
| sat_info quiet (tf_none, info.in_decl); |
| tree result = constraint_satisfaction_value (t, args, quiet); |
| if (result == boolean_true_node) |
| return boolean_true_node; |
| |
| if (result == boolean_false_node |
| && info.diagnose_unsatisfaction_p ()) |
| { |
| tree expr = TREE_OPERAND (t, 0); |
| location_t loc = cp_expr_location (t); |
| if (diagnosing_failed_constraint::replay_errors_p ()) |
| { |
| /* Replay the substitution error. */ |
| inform (loc, "nested requirement %qE is not satisfied, because", expr); |
| constraint_satisfaction_value (t, args, info); |
| } |
| else |
| inform (loc, "nested requirement %qE is not satisfied", expr); |
| } |
| |
| return error_mark_node; |
| } |
| |
| /* Substitute ARGS into the requirement T. */ |
| |
| static tree |
| tsubst_requirement (tree t, tree args, sat_info info) |
| { |
| iloc_sentinel loc_s (cp_expr_location (t)); |
| switch (TREE_CODE (t)) |
| { |
| case SIMPLE_REQ: |
| return tsubst_simple_requirement (t, args, info); |
| case TYPE_REQ: |
| return tsubst_type_requirement (t, args, info); |
| case COMPOUND_REQ: |
| return tsubst_compound_requirement (t, args, info); |
| case NESTED_REQ: |
| return tsubst_nested_requirement (t, args, info); |
| default: |
| break; |
| } |
| gcc_unreachable (); |
| } |
| |
| static tree |
| declare_constraint_vars (tree parms, tree vars) |
| { |
| tree s = vars; |
| for (tree t = parms; t; t = DECL_CHAIN (t)) |
| { |
| if (DECL_PACK_P (t)) |
| { |
| tree pack = extract_fnparm_pack (t, &s); |
| register_local_specialization (pack, t); |
| } |
| else |
| { |
| register_local_specialization (s, t); |
| s = DECL_CHAIN (s); |
| } |
| } |
| return vars; |
| } |
| |
| /* Substitute through as if checking function parameter types. This |
| will diagnose common parameter type errors. Returns error_mark_node |
| if an error occurred. */ |
| |
| static tree |
| check_constraint_variables (tree t, tree args, subst_info info) |
| { |
| tree types = NULL_TREE; |
| tree p = t; |
| while (p && !VOID_TYPE_P (p)) |
| { |
| types = tree_cons (NULL_TREE, TREE_TYPE (p), types); |
| p = TREE_CHAIN (p); |
| } |
| types = chainon (nreverse (types), void_list_node); |
| return tsubst_function_parms (types, args, info.complain, info.in_decl); |
| } |
| |
| /* A subroutine of tsubst_parameterized_constraint. Substitute ARGS |
| into the parameter list T, producing a sequence of constraint |
| variables, declared in the current scope. |
| |
| Note that the caller must establish a local specialization stack |
| prior to calling this function since this substitution will |
| declare the substituted parameters. */ |
| |
| static tree |
| tsubst_constraint_variables (tree t, tree args, subst_info info) |
| { |
| /* Perform a trial substitution to check for type errors. */ |
| tree parms = check_constraint_variables (t, args, info); |
| if (parms == error_mark_node) |
| return error_mark_node; |
| |
| /* Clear cp_unevaluated_operand across tsubst so that we get a proper chain |
| of PARM_DECLs. */ |
| int saved_unevaluated_operand = cp_unevaluated_operand; |
| cp_unevaluated_operand = 0; |
| tree vars = tsubst (t, args, info.complain, info.in_decl); |
| cp_unevaluated_operand = saved_unevaluated_operand; |
| if (vars == error_mark_node) |
| return error_mark_node; |
| return declare_constraint_vars (t, vars); |
| } |
| |
| /* Substitute ARGS into the requires-expression T. [8.4.7]p6. The |
| substitution of template arguments into a requires-expression |
| may result in the formation of invalid types or expressions |
| in its requirements ... In such cases, the expression evaluates |
| to false; it does not cause the program to be ill-formed. |
| |
| When substituting through a REQUIRES_EXPR as part of template |
| instantiation, we call this routine with info.quiet() true. |
| |
| When evaluating a REQUIRES_EXPR that appears outside a template in |
| cp_parser_requires_expression, we call this routine with |
| info.noisy() true. |
| |
| Finally, when diagnosing unsatisfaction from diagnose_atomic_constraint |
| and when diagnosing a false REQUIRES_EXPR via diagnose_constraints, |
| we call this routine with info.diagnose_unsatisfaction_p() true. */ |
| |
| static tree |
| tsubst_requires_expr (tree t, tree args, sat_info info) |
| { |
| local_specialization_stack stack (lss_copy); |
| |
| /* A requires-expression is an unevaluated context. */ |
| cp_unevaluated u; |
| |
| args = add_extra_args (REQUIRES_EXPR_EXTRA_ARGS (t), args, |
| info.complain, info.in_decl); |
| if (processing_template_decl) |
| { |
| /* We're partially instantiating a generic lambda. Substituting into |
| this requires-expression now may cause its requirements to get |
| checked out of order, so instead just remember the template |
| arguments and wait until we can substitute them all at once. */ |
| t = copy_node (t); |
| REQUIRES_EXPR_EXTRA_ARGS (t) = build_extra_args (t, args, info.complain); |
| return t; |
| } |
| |
| if (tree parms = REQUIRES_EXPR_PARMS (t)) |
| { |
| parms = tsubst_constraint_variables (parms, args, info); |
| if (parms == error_mark_node) |
| return boolean_false_node; |
| } |
| |
| tree result = boolean_true_node; |
| for (tree reqs = REQUIRES_EXPR_REQS (t); reqs; reqs = TREE_CHAIN (reqs)) |
| { |
| tree req = TREE_VALUE (reqs); |
| if (tsubst_requirement (req, args, info) == error_mark_node) |
| { |
| result = boolean_false_node; |
| if (info.diagnose_unsatisfaction_p ()) |
| /* Keep going so that we diagnose all failed requirements. */; |
| else |
| break; |
| } |
| } |
| return result; |
| } |
| |
| /* Public wrapper for the above. */ |
| |
| tree |
| tsubst_requires_expr (tree t, tree args, |
| tsubst_flags_t complain, tree in_decl) |
| { |
| sat_info info (complain, in_decl); |
| return tsubst_requires_expr (t, args, info); |
| } |
| |
| /* Substitute ARGS into the constraint information CI, producing a new |
| constraint record. */ |
| |
| tree |
| tsubst_constraint_info (tree t, tree args, |
| tsubst_flags_t complain, tree in_decl) |
| { |
| if (!t || t == error_mark_node || !check_constraint_info (t)) |
| return NULL_TREE; |
| |
| tree tr = tsubst_constraint (CI_TEMPLATE_REQS (t), args, complain, in_decl); |
| tree dr = tsubst_constraint (CI_DECLARATOR_REQS (t), args, complain, in_decl); |
| return build_constraints (tr, dr); |
| } |
| |
| /* Substitute through a parameter mapping, in order to get the actual |
| arguments used to instantiate an atomic constraint. This may fail |
| if the substitution into arguments produces something ill-formed. */ |
| |
| static tree |
| tsubst_parameter_mapping (tree map, tree args, subst_info info) |
| { |
| if (!map) |
| return NULL_TREE; |
| |
| tsubst_flags_t complain = info.complain; |
| tree in_decl = info.in_decl; |
| |
| tree result = NULL_TREE; |
| for (tree p = map; p; p = TREE_CHAIN (p)) |
| { |
| if (p == error_mark_node) |
| return error_mark_node; |
| tree parm = TREE_VALUE (p); |
| tree arg = TREE_PURPOSE (p); |
| tree new_arg; |
| if (ARGUMENT_PACK_P (arg)) |
| new_arg = tsubst_argument_pack (arg, args, complain, in_decl); |
| else |
| { |
| new_arg = tsubst_template_arg (arg, args, complain, in_decl); |
| if (TYPE_P (new_arg)) |
| new_arg = canonicalize_type_argument (new_arg, complain); |
| } |
| 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); |
| } |
| } |
| if (new_arg == error_mark_node) |
| return error_mark_node; |
| |
| result = tree_cons (new_arg, parm, result); |
| } |
| return nreverse (result); |
| } |
| |
| tree |
| tsubst_parameter_mapping (tree map, tree args, tsubst_flags_t complain, tree in_decl) |
| { |
| return tsubst_parameter_mapping (map, args, subst_info (complain, in_decl)); |
| } |
| |
| /*--------------------------------------------------------------------------- |
| Constraint satisfaction |
| ---------------------------------------------------------------------------*/ |
| |
| /* True if we are currently satisfying a constraint. */ |
| |
| static bool satisfying_constraint; |
| |
| /* A vector of incomplete types (and of declarations with undeduced return type), |
| appended to by note_failed_type_completion_for_satisfaction. The |
| satisfaction caches use this in order to keep track of "potentially unstable" |
| satisfaction results. |
| |
| Since references to entries in this vector are stored only in the |
| GC-deletable sat_cache, it's safe to make this deletable as well. */ |
| |
| static GTY((deletable)) vec<tree, va_gc> *failed_type_completions; |
| |
| /* Called whenever a type completion (or return type deduction) failure occurs |
| that definitely affects the meaning of the program, by e.g. inducing |
| substitution failure. */ |
| |
| void |
| note_failed_type_completion_for_satisfaction (tree t) |
| { |
| if (satisfying_constraint) |
| { |
| gcc_checking_assert ((TYPE_P (t) && !COMPLETE_TYPE_P (t)) |
| || (DECL_P (t) && undeduced_auto_decl (t))); |
| vec_safe_push (failed_type_completions, t); |
| } |
| } |
| |
| /* Returns true if the range [BEGIN, END) of elements within the |
| failed_type_completions vector contains a complete type (or a |
| declaration with a non-placeholder return type). */ |
| |
| static bool |
| some_type_complete_p (int begin, int end) |
| { |
| for (int i = begin; i < end; i++) |
| { |
| tree t = (*failed_type_completions)[i]; |
| if (TYPE_P (t) && COMPLETE_TYPE_P (t)) |
| return true; |
| if (DECL_P (t) && !undeduced_auto_decl (t)) |
| return true; |
| } |
| return false; |
| } |
| |
| /* Hash functions and data types for satisfaction cache entries. */ |
| |
| struct GTY((for_user)) sat_entry |
| { |
| /* The relevant ATOMIC_CONSTR. */ |
| tree atom; |
| |
| /* The relevant template arguments. */ |
| tree args; |
| |
| /* The result of satisfaction of ATOM+ARGS. |
| This is either boolean_true_node, boolean_false_node or error_mark_node, |
| where error_mark_node indicates ill-formed satisfaction. |
| It's set to NULL_TREE while computing satisfaction of ATOM+ARGS for |
| the first time. */ |
| tree result; |
| |
| /* The value of input_location when satisfaction of ATOM+ARGS was first |
| performed. */ |
| location_t location; |
| |
| /* The range of elements appended to the failed_type_completions vector |
| during computation of this satisfaction result, encoded as a begin/end |
| pair of offsets. */ |
| int ftc_begin, ftc_end; |
| |
| /* True if we want to diagnose the above instability when it's detected. |
| We don't always want to do so, in order to avoid emitting duplicate |
| diagnostics in some cases. */ |
| bool diagnose_instability; |
| |
| /* True if we're in the middle of computing this satisfaction result. |
| Used during both quiet and noisy satisfaction to detect self-recursive |
| satisfaction. */ |
| bool evaluating; |
| }; |
| |
| struct sat_hasher : ggc_ptr_hash<sat_entry> |
| { |
| static hashval_t hash (sat_entry *e) |
| { |
| if (ATOMIC_CONSTR_MAP_INSTANTIATED_P (e->atom)) |
| { |
| /* Atoms with instantiated mappings are built during satisfaction. |
| They live only inside the sat_cache, and we build one to query |
| the cache with each time we instantiate a mapping. */ |
| gcc_assert (!e->args); |
| return hash_atomic_constraint (e->atom); |
| } |
| |
| /* Atoms with uninstantiated mappings are built during normalization. |
| Since normalize_atom caches the atoms it returns, we can assume |
| pointer-based identity for fast hashing and comparison. Even if this |
| assumption is violated, that's okay, we'll just get a cache miss. */ |
| hashval_t value = htab_hash_pointer (e->atom); |
| |
| if (tree map = ATOMIC_CONSTR_MAP (e->atom)) |
| /* Only the parameters that are used in the targets of the mapping |
| affect the satisfaction value of the atom. So we consider only |
| the arguments for these parameters, and ignore the rest. */ |
| for (tree target_parms = TREE_TYPE (map); |
| target_parms; |
| target_parms = TREE_CHAIN (target_parms)) |
| { |
| int level, index; |
| tree parm = TREE_VALUE (target_parms); |
| template_parm_level_and_index (parm, &level, &index); |
| tree arg = TMPL_ARG (e->args, level, index); |
| value = iterative_hash_template_arg (arg, value); |
| } |
| return value; |
| } |
| |
| static bool equal (sat_entry *e1, sat_entry *e2) |
| { |
| if (ATOMIC_CONSTR_MAP_INSTANTIATED_P (e1->atom) |
| != ATOMIC_CONSTR_MAP_INSTANTIATED_P (e2->atom)) |
| return false; |
| |
| /* See sat_hasher::hash. */ |
| if (ATOMIC_CONSTR_MAP_INSTANTIATED_P (e1->atom)) |
| { |
| gcc_assert (!e1->args && !e2->args); |
| return atomic_constraints_identical_p (e1->atom, e2->atom); |
| } |
| |
| if (e1->atom != e2->atom) |
| return false; |
| |
| if (tree map = ATOMIC_CONSTR_MAP (e1->atom)) |
| for (tree target_parms = TREE_TYPE (map); |
| target_parms; |
| target_parms = TREE_CHAIN (target_parms)) |
| { |
| int level, index; |
| tree parm = TREE_VALUE (target_parms); |
| template_parm_level_and_index (parm, &level, &index); |
| tree arg1 = TMPL_ARG (e1->args, level, index); |
| tree arg2 = TMPL_ARG (e2->args, level, index); |
| if (!template_args_equal (arg1, arg2)) |
| return false; |
| } |
| return true; |
| } |
| }; |
| |
| /* Cache the result of satisfy_atom. */ |
| static GTY((deletable)) hash_table<sat_hasher> *sat_cache; |
| |
| /* Cache the result of satisfy_declaration_constraints. */ |
| static GTY((deletable)) hash_map<tree, tree> *decl_satisfied_cache; |
| |
| /* A tool used by satisfy_atom to help manage satisfaction caching and to |
| diagnose "unstable" satisfaction values. We insert into the cache only |
| when performing satisfaction quietly. */ |
| |
| struct satisfaction_cache |
| { |
| satisfaction_cache (tree, tree, sat_info); |
| tree get (); |
| tree save (tree); |
| |
| sat_entry *entry; |
| sat_info info; |
| int ftc_begin; |
| }; |
| |
| /* Constructor for the satisfaction_cache class. We're performing satisfaction |
| of ATOM+ARGS according to INFO. */ |
| |
| satisfaction_cache |
| ::satisfaction_cache (tree atom, tree args, sat_info info) |
| : entry(nullptr), info(info), ftc_begin(-1) |
| { |
| if (!sat_cache) |
| sat_cache = hash_table<sat_hasher>::create_ggc (31); |
| |
| /* When noisy, we query the satisfaction cache in order to diagnose |
| "unstable" satisfaction values. */ |
| if (info.noisy ()) |
| { |
| /* When noisy, constraints have been re-normalized, and that breaks the |
| pointer-based identity assumption of sat_cache (for atoms with |
| uninstantiated mappings). So undo this re-normalization by looking in |
| the atom_cache for the corresponding atom that was used during quiet |
| satisfaction. */ |
| if (!ATOMIC_CONSTR_MAP_INSTANTIATED_P (atom)) |
| { |
| if (tree found = atom_cache->find (atom)) |
| atom = found; |
| else |
| /* The lookup should always succeed, but if it fails then let's |
| just leave 'entry' empty, effectively disabling the cache. */ |
| return; |
| } |
| } |
| |
| /* Look up or create the corresponding satisfaction entry. */ |
| sat_entry elt; |
| elt.atom = atom; |
| elt.args = args; |
| sat_entry **slot = sat_cache->find_slot (&elt, INSERT); |
| if (*slot) |
| entry = *slot; |
| else if (info.quiet ()) |
| { |
| entry = ggc_alloc<sat_entry> (); |
| entry->atom = atom; |
| entry->args = args; |
| entry->result = NULL_TREE; |
| entry->location = input_location; |
| entry->ftc_begin = entry->ftc_end = -1; |
| entry->diagnose_instability = false; |
| if (ATOMIC_CONSTR_MAP_INSTANTIATED_P (atom)) |
| /* We always want to diagnose instability of an atom with an |
| instantiated parameter mapping. For atoms with an uninstantiated |
| mapping, we set this flag (in satisfy_atom) only if substitution |
| into its mapping previously failed. */ |
| entry->diagnose_instability = true; |
| entry->evaluating = false; |
| *slot = entry; |
| } |
| else |
| /* We shouldn't get here, but if we do, let's just leave 'entry' |
| empty, effectively disabling the cache. */ |
| return; |
| } |
| |
| /* Returns the cached satisfaction result if we have one and we're not |
| recomputing the satisfaction result from scratch. Otherwise returns |
| NULL_TREE. */ |
| |
| tree |
| satisfaction_cache::get () |
| { |
| if (!entry) |
| return NULL_TREE; |
| |
| if (entry->evaluating) |
| { |
| /* If we get here, it means satisfaction is self-recursive. */ |
| gcc_checking_assert (!entry->result); |
| if (info.noisy ()) |
| error_at (EXPR_LOCATION (ATOMIC_CONSTR_EXPR (entry->atom)), |
| "satisfaction of atomic constraint %qE depends on itself", |
| entry->atom); |
| return error_mark_node; |
| } |
| |
| /* This satisfaction result is "potentially unstable" if a type for which |
| type completion failed during its earlier computation is now complete. */ |
| bool maybe_unstable = some_type_complete_p (entry->ftc_begin, |
| entry->ftc_end); |
| |
| if (info.noisy () || maybe_unstable || !entry->result) |
| { |
| /* We're computing the satisfaction result from scratch. */ |
| entry->evaluating = true; |
| ftc_begin = vec_safe_length (failed_type_completions); |
| return NULL_TREE; |
| } |
| else |
| return entry->result; |
| } |
| |
| /* RESULT is the computed satisfaction result. If RESULT differs from the |
| previously cached result, this routine issues an appropriate error. |
| Otherwise, when evaluating quietly, updates the cache appropriately. */ |
| |
| tree |
| satisfaction_cache::save (tree result) |
| { |
| if (!entry) |
| return result; |
| |
| gcc_checking_assert (entry->evaluating); |
| entry->evaluating = false; |
| |
| if (entry->result && result != entry->result) |
| { |
| if (info.quiet ()) |
| /* Return error_mark_node to force satisfaction to get replayed |
| noisily. */ |
| return error_mark_node; |
| else |
| { |
| if (entry->diagnose_instability) |
| { |
| auto_diagnostic_group d; |
| error_at (EXPR_LOCATION (ATOMIC_CONSTR_EXPR (entry->atom)), |
| "satisfaction value of atomic constraint %qE changed " |
| "from %qE to %qE", entry->atom, entry->result, result); |
| inform (entry->location, |
| "satisfaction value first evaluated to %qE from here", |
| entry->result); |
| } |
| /* For sake of error recovery, allow this latest satisfaction result |
| to prevail. */ |
| entry->result = result; |
| return result; |
| } |
| } |
| |
| if (info.quiet ()) |
| { |
| entry->result = result; |
| /* Store into this entry the list of relevant failed type completions |
| that occurred during (re)computation of the satisfaction result. */ |
| gcc_checking_assert (ftc_begin != -1); |
| entry->ftc_begin = ftc_begin; |
| entry->ftc_end = vec_safe_length (failed_type_completions); |
| } |
| |
| return result; |
| } |
| |
| /* Substitute ARGS into constraint-expression T during instantiation of |
| a member of a class template. */ |
| |
| tree |
| tsubst_constraint (tree t, tree args, tsubst_flags_t complain, tree in_decl) |
| { |
| /* We also don't want to evaluate concept-checks when substituting the |
| constraint-expressions of a declaration. */ |
| processing_constraint_expression_sentinel s; |
| cp_unevaluated u; |
| tree expr = tsubst_expr (t, args, complain, in_decl, false); |
| return expr; |
| } |
| |
| static tree satisfy_constraint_r (tree, tree, sat_info info); |
| |
| /* Compute the satisfaction of a conjunction. */ |
| |
| static tree |
| satisfy_conjunction (tree t, tree args, sat_info info) |
| { |
| tree lhs = satisfy_constraint_r (TREE_OPERAND (t, 0), args, info); |
| if (lhs == error_mark_node || lhs == boolean_false_node) |
| return lhs; |
| return satisfy_constraint_r (TREE_OPERAND (t, 1), args, info); |
| } |
| |
| /* The current depth at which we're replaying an error during recursive |
| diagnosis of a constraint satisfaction failure. */ |
| |
| static int current_constraint_diagnosis_depth; |
| |
| /* Whether CURRENT_CONSTRAINT_DIAGNOSIS_DEPTH has ever exceeded |
| CONCEPTS_DIAGNOSTICS_MAX_DEPTH during recursive diagnosis of a constraint |
| satisfaction error. */ |
| |
| static bool concepts_diagnostics_max_depth_exceeded_p; |
| |
| /* Recursive subroutine of collect_operands_of_disjunction. T is a normalized |
| subexpression of a constraint (composed of CONJ_CONSTRs and DISJ_CONSTRs) |
| and E is the corresponding unnormalized subexpression (composed of |
| TRUTH_ANDIF_EXPRs and TRUTH_ORIF_EXPRs). */ |
| |
| static void |
| collect_operands_of_disjunction_r (tree t, tree e, |
| auto_vec<tree_pair> *operands) |
| { |
| if (TREE_CODE (e) == TRUTH_ORIF_EXPR) |
| { |
| collect_operands_of_disjunction_r (TREE_OPERAND (t, 0), |
| TREE_OPERAND (e, 0), operands); |
| collect_operands_of_disjunction_r (TREE_OPERAND (t, 1), |
| TREE_OPERAND (e, 1), operands); |
| } |
| else |
| { |
| tree_pair p = std::make_pair (t, e); |
| operands->safe_push (p); |
| } |
| } |
| |
| /* Recursively collect the normalized and unnormalized operands of the |
| disjunction T and append them to OPERANDS in order. */ |
| |
| static void |
| collect_operands_of_disjunction (tree t, auto_vec<tree_pair> *operands) |
| { |
| collect_operands_of_disjunction_r (t, CONSTR_EXPR (t), operands); |
| } |
| |
| /* Compute the satisfaction of a disjunction. */ |
| |
| static tree |
| satisfy_disjunction (tree t, tree args, sat_info info) |
| { |
| /* Evaluate each operand with unsatisfaction diagnostics disabled. */ |
| sat_info sub = info; |
| sub.diagnose_unsatisfaction = false; |
| |
| tree lhs = satisfy_constraint_r (TREE_OPERAND (t, 0), args, sub); |
| if (lhs == boolean_true_node || lhs == error_mark_node) |
| return lhs; |
| |
| tree rhs = satisfy_constraint_r (TREE_OPERAND (t, 1), args, sub); |
| if (rhs == boolean_true_node || rhs == error_mark_node) |
| return rhs; |
| |
| /* Both branches evaluated to false. Explain the satisfaction failure in |
| each branch. */ |
| if (info.diagnose_unsatisfaction_p ()) |
| { |
| diagnosing_failed_constraint failure (t, args, info.noisy ()); |
| cp_expr disj_expr = CONSTR_EXPR (t); |
| inform (disj_expr.get_location (), |
| "no operand of the disjunction is satisfied"); |
| if (diagnosing_failed_constraint::replay_errors_p ()) |
| { |
| /* Replay the error in each branch of the disjunction. */ |
| auto_vec<tree_pair> operands; |
| collect_operands_of_disjunction (t, &operands); |
| for (unsigned i = 0; i < operands.length (); i++) |
| { |
| tree norm_op = operands[i].first; |
| tree op = operands[i].second; |
| location_t loc = make_location (cp_expr_location (op), |
| disj_expr.get_start (), |
| disj_expr.get_finish ()); |
| inform (loc, "the operand %qE is unsatisfied because", op); |
| satisfy_constraint_r (norm_op, args, info); |
| } |
| } |
| } |
| |
| return boolean_false_node; |
| } |
| |
| /* Ensures that T is a truth value and not (accidentally, as sometimes |
| happens) an integer value. */ |
| |
| tree |
| satisfaction_value (tree t) |
| { |
| if (t == error_mark_node || t == boolean_true_node || t == boolean_false_node) |
| return t; |
| |
| gcc_assert (TREE_CODE (t) == INTEGER_CST |
| && same_type_ignoring_top_level_qualifiers_p (TREE_TYPE (t), |
| boolean_type_node)); |
| if (integer_zerop (t)) |
| return boolean_false_node; |
| else |
| return boolean_true_node; |
| } |
| |
| /* Build a new template argument vector corresponding to the parameter |
| mapping of the atomic constraint T, using arguments from ARGS. */ |
| |
| static tree |
| get_mapped_args (tree t, tree args) |
| { |
| tree map = ATOMIC_CONSTR_MAP (t); |
| |
| /* No map, no arguments. */ |
| if (!map) |
| return NULL_TREE; |
| |
| /* Determine the depth of the resulting argument vector. */ |
| int depth; |
| if (ATOMIC_CONSTR_EXPR_FROM_CONCEPT_P (t)) |
| /* The expression of this atomic constraint comes from a concept definition, |
| whose template depth is always one, so the resulting argument vector |
| will also have depth one. */ |
| depth = 1; |
| else |
| /* Otherwise, the expression of this atomic constraint comes from |
| the context of the constrained entity, whose template depth is that |
| of ARGS. */ |
| depth = TMPL_ARGS_DEPTH (args); |
| |
| /* Place each argument at its corresponding position in the argument |
| list. Note that the list will be sparse (not all arguments supplied), |
| but instantiation is guaranteed to only use the parameters in the |
| mapping, so null arguments would never be used. */ |
| auto_vec< vec<tree> > lists (depth); |
| lists.quick_grow_cleared (depth); |
| for (tree p = map; p; p = TREE_CHAIN (p)) |
| { |
| int level; |
| int index; |
| template_parm_level_and_index (TREE_VALUE (p), &level, &index); |
| |
| /* Insert the argument into its corresponding position. */ |
| vec<tree> &list = lists[level - 1]; |
| if (index >= (int)list.length ()) |
| list.safe_grow_cleared (index + 1, /*exact=*/false); |
| list[index] = TREE_PURPOSE (p); |
| } |
| |
| /* Build the new argument list. */ |
| args = make_tree_vec (lists.length ()); |
| for (unsigned i = 0; i != lists.length (); ++i) |
| { |
| vec<tree> &list = lists[i]; |
| tree level = make_tree_vec (list.length ()); |
| for (unsigned j = 0; j < list.length(); ++j) |
| TREE_VEC_ELT (level, j) = list[j]; |
| SET_TMPL_ARGS_LEVEL (args, i + 1, level); |
| list.release (); |
| } |
| SET_NON_DEFAULT_TEMPLATE_ARGS_COUNT (args, 0); |
| |
| if (TMPL_ARGS_HAVE_MULTIPLE_LEVELS (args) |
| && TMPL_ARGS_DEPTH (args) == 1) |
| { |
| /* Get rid of the redundant outer TREE_VEC. */ |
| tree level = TMPL_ARGS_LEVEL (args, 1); |
| ggc_free (args); |
| args = level; |
| } |
| |
| return args; |
| } |
| |
| static void diagnose_atomic_constraint (tree, tree, tree, sat_info); |
| |
| /* Compute the satisfaction of an atomic constraint. */ |
| |
| static tree |
| satisfy_atom (tree t, tree args, sat_info info) |
| { |
| /* In case there is a diagnostic, we want to establish the context |
| prior to printing errors. If no errors occur, this context is |
| removed before returning. */ |
| diagnosing_failed_constraint failure (t, args, info.noisy ()); |
| |
| satisfaction_cache cache (t, args, info); |
| if (tree r = cache.get ()) |
| return r; |
| |
| /* Perform substitution quietly. */ |
| subst_info quiet (tf_none, NULL_TREE); |
| |
| /* Instantiate the parameter mapping. */ |
| tree map = tsubst_parameter_mapping (ATOMIC_CONSTR_MAP (t), args, quiet); |
| if (map == error_mark_node) |
| { |
| /* If instantiation of the parameter mapping fails, the constraint is |
| not satisfied. Replay the substitution. */ |
| if (info.diagnose_unsatisfaction_p ()) |
| tsubst_parameter_mapping (ATOMIC_CONSTR_MAP (t), args, info); |
| if (info.quiet ()) |
| /* Since instantiation of the parameter mapping failed, we |
| want to diagnose potential instability of this satisfaction |
| result. */ |
| cache.entry->diagnose_instability = true; |
| return cache.save (boolean_false_node); |
| } |
| |
| /* Now build a new atom using the instantiated mapping. We use |
| this atom as a second key to the satisfaction cache, and we |
| also pass it to diagnose_atomic_constraint so that diagnostics |
| which refer to the atom display the instantiated mapping. */ |
| t = copy_node (t); |
| ATOMIC_CONSTR_MAP (t) = map; |
| gcc_assert (!ATOMIC_CONSTR_MAP_INSTANTIATED_P (t)); |
| ATOMIC_CONSTR_MAP_INSTANTIATED_P (t) = true; |
| satisfaction_cache inst_cache (t, /*args=*/NULL_TREE, info); |
| if (tree r = inst_cache.get ()) |
| { |
| cache.entry->location = inst_cache.entry->location; |
| return cache.save (r); |
| } |
| |
| /* Rebuild the argument vector from the parameter mapping. */ |
| args = get_mapped_args (t, args); |
| |
| /* 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); |
| 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); |
| return cache.save (inst_cache.save (boolean_false_node)); |
| } |
| |
| /* [17.4.1.2] ... lvalue-to-rvalue conversion is performed as necessary, |
| and EXPR shall be a constant expression of type bool. */ |
| result = force_rvalue (result, info.complain); |
| if (result == error_mark_node) |
| return cache.save (inst_cache.save (error_mark_node)); |
| if (!same_type_p (TREE_TYPE (result), boolean_type_node)) |
| { |
| if (info.noisy ()) |
| diagnose_atomic_constraint (t, args, result, info); |
| return cache.save (inst_cache.save (error_mark_node)); |
| } |
| |
| /* Compute the value of the constraint. */ |
| if (info.noisy ()) |
| { |
| iloc_sentinel ils (EXPR_LOCATION (result)); |
| result = cxx_constant_value (result); |
| } |
| else |
| { |
| result = maybe_constant_value (result, NULL_TREE, |
| /*manifestly_const_eval=*/true); |
| if (!TREE_CONSTANT (result)) |
| result = error_mark_node; |
| } |
| result = satisfaction_value (result); |
| if (result == boolean_false_node && info.diagnose_unsatisfaction_p ()) |
| diagnose_atomic_constraint (t, args, result, info); |
| |
| return cache.save (inst_cache.save (result)); |
| } |
| |
| /* Determine if the normalized constraint T is satisfied. |
| Returns boolean_true_node if the expression/constraint is |
| satisfied, boolean_false_node if not, and error_mark_node |
| if the there was an error evaluating the constraint. |
| |
| The parameter mapping of atomic constraints is simply the |
| set of template arguments that will be substituted into |
| the expression, regardless of template parameters appearing |
| withing. Whether a template argument is used in the atomic |
| constraint only matters for subsumption. */ |
| |
| static tree |
| satisfy_constraint_r (tree t, tree args, sat_info info) |
| { |
| if (t == error_mark_node) |
| return error_mark_node; |
| |
| switch (TREE_CODE (t)) |
| { |
| case CONJ_CONSTR: |
| return satisfy_conjunction (t, args, info); |
| case DISJ_CONSTR: |
| return satisfy_disjunction (t, args, info); |
| case ATOMIC_CONSTR: |
| return satisfy_atom (t, args, info); |
| default: |
| gcc_unreachable (); |
| } |
| } |
| |
| /* Check that the normalized constraint T is satisfied for ARGS. */ |
| |
| static tree |
| satisfy_normalized_constraints (tree t, tree args, sat_info info) |
| { |
| auto_timevar time (TV_CONSTRAINT_SAT); |
| |
| auto ovr = make_temp_override (satisfying_constraint, true); |
| |
| /* Turn off template processing. Constraint satisfaction only applies |
| to non-dependent terms, so we want to ensure full checking here. */ |
| processing_template_decl_sentinel proc (true); |
| |
| /* We need to check access during satisfaction. */ |
| deferring_access_check_sentinel acs (dk_no_deferred); |
| |
| /* Constraints are unevaluated operands. */ |
| cp_unevaluated u; |
| |
| return satisfy_constraint_r (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 the constraints of T using ARGS, returning a satisfaction value. |
| Here, T can be a concept-id, nested-requirement, placeholder 'auto', or |
| requires-expression. */ |
| |
| static tree |
| satisfy_nondeclaration_constraints (tree t, tree args, sat_info info) |
| { |
| if (t == error_mark_node) |
| return error_mark_node; |
| |
| /* Handle REQUIRES_EXPR directly, bypassing satisfaction. */ |
| if (TREE_CODE (t) == REQUIRES_EXPR) |
| { |
| auto ovr = make_temp_override (current_constraint_diagnosis_depth); |
| if (info.noisy ()) |
| ++current_constraint_diagnosis_depth; |
| return tsubst_requires_expr (t, args, info); |
| } |
| |
| /* Get the normalized constraints. */ |
| tree norm; |
| if (concept_check_p (t)) |
| { |
| gcc_assert (!args); |
| tree id = unpack_concept_check (t); |
| args = TREE_OPERAND (id, 1); |
| tree tmpl = get_concept_check_template (id); |
| norm = normalize_concept_definition (tmpl, info.noisy ()); |
| } |
| else if (TREE_CODE (t) == NESTED_REQ) |
| { |
| norm_info ninfo (info.noisy () ? tf_norm : tf_none); |
| /* The TREE_TYPE contains the set of template parameters that were in |
| scope for this nested requirement; use them as the initial template |
| parameters for normalization. */ |
| ninfo.initial_parms = TREE_TYPE (t); |
| norm = normalize_constraint_expression (TREE_OPERAND (t, 0), ninfo); |
| } |
| else if (is_auto (t)) |
| { |
| norm = normalize_placeholder_type_constraints (t, info.noisy ()); |
| if (!norm) |
| return boolean_true_node; |
| } |
| else |
| gcc_unreachable (); |
| |
| /* Perform satisfaction. */ |
| return satisfy_normalized_constraints (norm, args, info); |
| } |
| |
| /* Evaluate the associated constraints of the template specialization T |
| according to INFO, returning a satisfaction value. */ |
| |
| static tree |
| satisfy_declaration_constraints (tree t, sat_info info) |
| { |
| gcc_assert (DECL_P (t) && TREE_CODE (t) != TEMPLATE_DECL); |
| const tree saved_t = t; |
| |
| /* For inherited constructors, consider the original declaration; |
| it has the correct template information attached. */ |
| t = strip_inheriting_ctors (t); |
| tree inh_ctor_targs = NULL_TREE; |
| if (t != saved_t) |
| if (tree ti = DECL_TEMPLATE_INFO (saved_t)) |
| /* The inherited constructor points to an instantiation of a constructor |
| template; remember its template arguments. */ |
| inh_ctor_targs = TI_ARGS (ti); |
| |
| /* Update the declaration for diagnostics. */ |
| info.in_decl = t; |
| |
| if (info.quiet ()) |
| if (tree *result = hash_map_safe_get (decl_satisfied_cache, saved_t)) |
| return *result; |
| |
| tree args = NULL_TREE; |
| if (tree ti = DECL_TEMPLATE_INFO (t)) |
| { |
| /* The initial parameter mapping is the complete set of |
| template arguments substituted into the declaration. */ |
| args = TI_ARGS (ti); |
| if (inh_ctor_targs) |
| args = add_outermost_template_args (args, inh_ctor_targs); |
| } |
| |
| if (regenerated_lambda_fn_p (t)) |
| { |
| /* The TI_ARGS of a regenerated lambda contains only the innermost |
| set of template arguments. Augment this with the outer template |
| arguments that were used to regenerate the lambda. */ |
| gcc_assert (!args || TMPL_ARGS_DEPTH (args) == 1); |
| tree regen_args = lambda_regenerating_args (t); |
| if (args) |
| args = add_to_template_args (regen_args, args); |
| else |
| 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)) |
| return boolean_true_node; |
| |
| /* Get the normalized constraints. */ |
| tree norm = get_normalized_constraints_from_decl (t, info.noisy ()); |
| |
| unsigned ftc_count = vec_safe_length (failed_type_completions); |
| |
| tree result = boolean_true_node; |
| if (norm) |
| { |
| if (!push_tinst_level (t)) |
| return result; |
| push_to_top_level (); |
| push_access_scope (t); |
| result = satisfy_normalized_constraints (norm, args, info); |
| pop_access_scope (t); |
| pop_from_top_level (); |
| pop_tinst_level (); |
| } |
| |
| /* True if this satisfaction is (heuristically) potentially unstable, i.e. |
| if its result may depend on where in the program it was performed. */ |
| bool maybe_unstable_satisfaction = false; |
| if (ftc_count != vec_safe_length (failed_type_completions)) |
| /* Type completion failure occurred during satisfaction. The satisfaction |
| result may (or may not) materially depend on the completeness of a type, |
| so we consider it potentially unstable. */ |
| maybe_unstable_satisfaction = true; |
| |
| if (maybe_unstable_satisfaction) |
| /* Don't cache potentially unstable satisfaction, to allow satisfy_atom |
| to check the stability the next time around. */; |
| else if (info.quiet ()) |
| hash_map_safe_put<hm_ggc> (decl_satisfied_cache, saved_t, result); |
| |
| return result; |
| } |
| |
| /* Evaluate the associated constraints of the template T using ARGS as the |
| innermost set of template arguments and according to INFO, returning a |
| satisfaction value. */ |
| |
| static tree |
| satisfy_declaration_constraints (tree t, tree args, sat_info info) |
| { |
| /* Update the declaration for diagnostics. */ |
| info.in_decl = t; |
| |
| gcc_assert (TREE_CODE (t) == TEMPLATE_DECL); |
| |
| if (regenerated_lambda_fn_p (t)) |
| { |
| /* As in the two-parameter version of this function. */ |
| gcc_assert (TMPL_ARGS_DEPTH (args) == 1); |
| tree lambda = CLASSTYPE_LAMBDA_EXPR (DECL_CONTEXT (t)); |
| tree outer_args = TI_ARGS (LAMBDA_EXPR_REGEN_INFO (lambda)); |
| args = add_to_template_args (outer_args, args); |
| } |
| 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)) |
| return boolean_true_node; |
| |
| tree result = boolean_true_node; |
| if (tree norm = get_normalized_constraints_from_decl (t, info.noisy ())) |
| { |
| if (!push_tinst_level (t, args)) |
| return result; |
| tree pattern = DECL_TEMPLATE_RESULT (t); |
| push_to_top_level (); |
| push_access_scope (pattern); |
| result = satisfy_normalized_constraints (norm, args, info); |
| pop_access_scope (pattern); |
| pop_from_top_level (); |
| pop_tinst_level (); |
| } |
| |
| return result; |
| } |
| |
| /* A wrapper around satisfy_declaration_constraints and |
| satisfy_nondeclaration_constraints which additionally replays |
| quiet ill-formed satisfaction noisily, so that ill-formed |
| satisfaction always gets diagnosed. */ |
| |
| static tree |
| constraint_satisfaction_value (tree t, tree args, sat_info info) |
| { |
| tree r; |
| if (DECL_P (t)) |
| { |
| if (args) |
| r = satisfy_declaration_constraints (t, args, info); |
| else |
| r = satisfy_declaration_constraints (t, info); |
| } |
| else |
| r = satisfy_nondeclaration_constraints (t, args, info); |
| if (r == error_mark_node && info.quiet () |
| && !(DECL_P (t) && warning_suppressed_p (t))) |
| { |
| /* Replay the error noisily. */ |
| sat_info noisy (tf_warning_or_error, info.in_decl); |
| constraint_satisfaction_value (t, args, noisy); |
| if (DECL_P (t) && !args) |
| /* Avoid giving these errors again. */ |
| suppress_warning (t); |
| } |
| return r; |
| } |
| |
| /* True iff the result of satisfying T using ARGS is BOOLEAN_TRUE_NODE |
| and false otherwise, even in the case of errors. |
| |
| Here, T can be: |
| - a template declaration |
| - a template specialization (in which case ARGS must be empty) |
| - a concept-id (in which case ARGS must be empty) |
| - a nested-requirement |
| - a placeholder 'auto' |
| - a requires-expression. */ |
| |
| bool |
| constraints_satisfied_p (tree t, tree args/*= NULL_TREE */)<
|