| /* Processing rules for constraints. |
| Copyright (C) 2013-2019 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" |
| |
| /*--------------------------------------------------------------------------- |
| Operations on constraints |
| ---------------------------------------------------------------------------*/ |
| |
| /* Returns true if C is a constraint tree code. Note that ERROR_MARK |
| is a valid constraint. */ |
| |
| static inline bool |
| constraint_p (tree_code c) |
| { |
| return ((PRED_CONSTR <= c && c <= DISJ_CONSTR) |
| || c == EXPR_PACK_EXPANSION |
| || c == ERROR_MARK); |
| } |
| |
| /* Returns true if T is a constraint. Note that error_mark_node |
| is a valid constraint. */ |
| |
| bool |
| constraint_p (tree t) |
| { |
| return constraint_p (TREE_CODE (t)); |
| } |
| |
| /* Returns the conjunction of two constraints A and B. Note that |
| conjoining a non-null constraint with NULL_TREE is an identity |
| operation. That is, for non-null A, |
| |
| conjoin_constraints(a, NULL_TREE) == a |
| |
| and |
| |
| conjoin_constraints (NULL_TREE, a) == a |
| |
| If both A and B are NULL_TREE, the result is also NULL_TREE. */ |
| |
| tree |
| conjoin_constraints (tree a, tree b) |
| { |
| gcc_assert (a ? constraint_p (a) : true); |
| gcc_assert (b ? constraint_p (b) : true); |
| if (a) |
| return b ? build_nt (CONJ_CONSTR, a, b) : a; |
| else if (b) |
| return b; |
| else |
| return NULL_TREE; |
| } |
| |
| /* Transform the vector of expressions in the T into a conjunction |
| of requirements. T must be a TREE_VEC. */ |
| |
| tree |
| conjoin_constraints (tree t) |
| { |
| gcc_assert (TREE_CODE (t) == TREE_VEC); |
| tree r = NULL_TREE; |
| for (int i = 0; i < TREE_VEC_LENGTH (t); ++i) |
| r = conjoin_constraints (r, TREE_VEC_ELT (t, i)); |
| return r; |
| } |
| |
| /* Returns true if T is a call expression to a function |
| concept. */ |
| |
| bool |
| function_concept_check_p (tree t) |
| { |
| gcc_assert (TREE_CODE (t) == CALL_EXPR); |
| tree fn = CALL_EXPR_FN (t); |
| if (fn != NULL_TREE |
| && TREE_CODE (fn) == TEMPLATE_ID_EXPR) |
| { |
| tree f1 = OVL_FIRST (TREE_OPERAND (fn, 0)); |
| if (TREE_CODE (f1) == TEMPLATE_DECL |
| && DECL_DECLARED_CONCEPT_P (DECL_TEMPLATE_RESULT (f1))) |
| return true; |
| } |
| return false; |
| } |
| |
| /* Returns true if any of the arguments in the template |
| argument list is a wildcard or wildcard pack. */ |
| |
| bool |
| contains_wildcard_p (tree args) |
| { |
| for (int i = 0; i < TREE_VEC_LENGTH (args); ++i) |
| { |
| tree arg = TREE_VEC_ELT (args, i); |
| if (TREE_CODE (arg) == WILDCARD_DECL) |
| return true; |
| } |
| return false; |
| } |
| |
| /* Build a new call expression, but don't actually generate a |
| new function call. We just want the tree, not the semantics. */ |
| |
| inline tree |
| build_call_check (tree id) |
| { |
| ++processing_template_decl; |
| vec<tree, va_gc> *fargs = make_tree_vector(); |
| tree call = finish_call_expr (id, &fargs, false, false, tf_none); |
| release_tree_vector (fargs); |
| --processing_template_decl; |
| return call; |
| } |
| |
| /* Build an expression that will check a variable concept. If any |
| argument contains a wildcard, don't try to finish the variable |
| template because we can't substitute into a non-existent |
| declaration. */ |
| |
| tree |
| build_variable_check (tree id) |
| { |
| gcc_assert (TREE_CODE (id) == TEMPLATE_ID_EXPR); |
| if (contains_wildcard_p (TREE_OPERAND (id, 1))) |
| return id; |
| |
| ++processing_template_decl; |
| tree var = finish_template_variable (id); |
| --processing_template_decl; |
| return var; |
| } |
| |
| /*--------------------------------------------------------------------------- |
| 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_constraint_check (tree ovl, tree args) |
| { |
| int nerrs = 0; |
| tree cands = NULL_TREE; |
| for (lkp_iterator iter (ovl); iter; ++iter) |
| { |
| // Get the next template overload. |
| 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 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_constraint_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_constraint_check (ovl, args); |
| } |
| |
| /* Returns a pair containing the checked variable concept |
| and its associated prototype parameter. The result |
| is a TREE_LIST whose TREE_VALUE is the variable concept |
| and whose TREE_PURPOSE is the prototype parameter. */ |
| |
| tree |
| resolve_variable_concept_check (tree id) |
| { |
| tree tmpl = TREE_OPERAND (id, 0); |
| tree args = TREE_OPERAND (id, 1); |
| |
| if (!variable_concept_p (tmpl)) |
| return NULL_TREE; |
| |
| /* Make sure that we have the right parameters before |
| assuming that it works. Note that failing to deduce |
| will result in diagnostics. */ |
| 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) |
| { |
| tree decl = DECL_TEMPLATE_RESULT (tmpl); |
| return build_tree_list (result, decl); |
| } |
| else |
| return error_mark_node; |
| } |
| |
| |
| /* 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 = NULL_TREE; |
| if (TREE_CODE (expr) == TEMPLATE_ID_EXPR) |
| info = resolve_variable_concept_check (expr); |
| else if (TREE_CODE (expr) == CALL_EXPR) |
| info = resolve_constraint_check (expr); |
| else |
| gcc_unreachable (); |
| |
| 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 expr) |
| { |
| tree info = NULL_TREE; |
| if (TREE_CODE (expr) == TEMPLATE_ID_EXPR) |
| info = resolve_variable_concept_check (expr); |
| else if (TREE_CODE (expr) == CALL_EXPR) |
| info = resolve_constraint_check (expr); |
| else |
| gcc_unreachable (); |
| |
| if (info && info != error_mark_node) |
| return TREE_PURPOSE (info); |
| return NULL_TREE; |
| } |
| |
| namespace { |
| |
| /*--------------------------------------------------------------------------- |
| Constraint implication learning |
| ---------------------------------------------------------------------------*/ |
| |
| /* The implication context determines how we memoize concept checks. |
| Given two checks C1 and C2, the direction of implication depends |
| on whether we are learning implications of a conjunction or disjunction. |
| For example: |
| |
| template<typename T> concept bool C = ...; |
| template<typenaem T> concept bool D = C<T> && true; |
| |
| From this, we can learn that D<T> implies C<T>. We cannot learn, |
| without further testing, that C<T> does not imply D<T>. If, for |
| example, C<T> were defined as true, then these constraints would |
| be logically equivalent. |
| |
| In rare cases, we may start with a logical equivalence. For example: |
| |
| template<typename T> concept bool C = ...; |
| template<typename T> concept bool D = C<T>; |
| |
| Here, we learn that C<T> implies D<T> and vice versa. */ |
| |
| enum implication_context |
| { |
| conjunction_cxt, /* C1 implies C2. */ |
| disjunction_cxt, /* C2 implies C1. */ |
| equivalence_cxt /* C1 implies C2, C2 implies C1. */ |
| }; |
| |
| void learn_implications(tree, tree, implication_context); |
| |
| void |
| learn_implication (tree parent, tree child, implication_context cxt) |
| { |
| switch (cxt) |
| { |
| case conjunction_cxt: |
| save_subsumption_result (parent, child, true); |
| break; |
| case disjunction_cxt: |
| save_subsumption_result (child, parent, true); |
| break; |
| case equivalence_cxt: |
| save_subsumption_result (parent, child, true); |
| save_subsumption_result (child, parent, true); |
| break; |
| } |
| } |
| |
| void |
| learn_logical_operation (tree parent, tree constr, implication_context cxt) |
| { |
| learn_implications (parent, TREE_OPERAND (constr, 0), cxt); |
| learn_implications (parent, TREE_OPERAND (constr, 1), cxt); |
| } |
| |
| void |
| learn_implications (tree parent, tree constr, implication_context cxt) |
| { |
| switch (TREE_CODE (constr)) |
| { |
| case CHECK_CONSTR: |
| return learn_implication (parent, constr, cxt); |
| |
| case CONJ_CONSTR: |
| if (cxt == disjunction_cxt) |
| return; |
| return learn_logical_operation (parent, constr, cxt); |
| |
| case DISJ_CONSTR: |
| if (cxt == conjunction_cxt) |
| return; |
| return learn_logical_operation (parent, constr, cxt); |
| |
| default: |
| break; |
| } |
| } |
| |
| /* Quickly scan the top-level constraints of CONSTR to learn and |
| cache logical relations between concepts. The search does not |
| include conjunctions of disjunctions or vice versa. */ |
| |
| void |
| learn_implications (tree tmpl, tree args, tree constr) |
| { |
| /* Don't memoize relations between non-dependent arguemnts. It's not |
| helpful. */ |
| if (!uses_template_parms (args)) |
| return; |
| |
| /* Build a check constraint for the purpose of caching. */ |
| tree parent = build_nt (CHECK_CONSTR, tmpl, args); |
| |
| /* Start learning based on the kind of the top-level contraint. */ |
| if (TREE_CODE (constr) == CONJ_CONSTR) |
| return learn_logical_operation (parent, constr, conjunction_cxt); |
| else if (TREE_CODE (constr) == DISJ_CONSTR) |
| return learn_logical_operation (parent, constr, disjunction_cxt); |
| else if (TREE_CODE (constr) == CHECK_CONSTR) |
| /* This is the rare concept alias case. */ |
| return learn_implication (parent, constr, equivalence_cxt); |
| } |
| |
| /*--------------------------------------------------------------------------- |
| Expansion of concept definitions |
| ---------------------------------------------------------------------------*/ |
| |
| /* Returns the expression of a function concept. */ |
| |
| 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. */ |
| |
| tree |
| get_variable_initializer (tree var) |
| { |
| tree init = DECL_INITIAL (var); |
| if (!init) |
| return error_mark_node; |
| return init; |
| } |
| |
| /* Returns the definition of a variable or function concept. */ |
| |
| tree |
| get_concept_definition (tree decl) |
| { |
| if (VAR_P (decl)) |
| return get_variable_initializer (decl); |
| else if (TREE_CODE (decl) == FUNCTION_DECL) |
| return get_returned_expression (decl); |
| gcc_unreachable (); |
| } |
| |
| int expansion_level = 0; |
| |
| struct expanding_concept_sentinel |
| { |
| expanding_concept_sentinel () |
| { |
| ++expansion_level; |
| } |
| |
| ~expanding_concept_sentinel() |
| { |
| --expansion_level; |
| } |
| }; |
| |
| |
| } /* namespace */ |
| |
| /* Returns true when a concept is being expanded. */ |
| |
| bool |
| expanding_concept() |
| { |
| return expansion_level > 0; |
| } |
| |
| /* Expand a concept declaration (not a template) and its arguments to |
| a constraint defined by the concept's initializer or definition. */ |
| |
| tree |
| expand_concept (tree decl, tree args) |
| { |
| expanding_concept_sentinel sentinel; |
| |
| if (TREE_CODE (decl) == TEMPLATE_DECL) |
| decl = DECL_TEMPLATE_RESULT (decl); |
| tree tmpl = DECL_TI_TEMPLATE (decl); |
| |
| /* Check for a previous specialization. */ |
| if (tree spec = get_concept_expansion (tmpl, args)) |
| return spec; |
| |
| /* Substitute the arguments to form a new definition expression. */ |
| tree def = get_concept_definition (decl); |
| |
| ++processing_template_decl; |
| tree result = tsubst_expr (def, args, tf_none, NULL_TREE, true); |
| --processing_template_decl; |
| if (result == error_mark_node) |
| return error_mark_node; |
| |
| /* And lastly, normalize it, check for implications, and save |
| the specialization for later. */ |
| tree norm = normalize_expression (result); |
| learn_implications (tmpl, args, norm); |
| return save_concept_expansion (tmpl, args, norm); |
| } |
| |
| |
| /*--------------------------------------------------------------------------- |
| Stepwise normalization of expressions |
| |
| This set of functions will transform an expression into a constraint |
| in a sequence of steps. Normalization does not not look into concept |
| definitions. |
| ---------------------------------------------------------------------------*/ |
| |
| /* Transform a logical-or or logical-and expression into either |
| a conjunction or disjunction. */ |
| |
| tree |
| normalize_logical_operation (tree t, tree_code c) |
| { |
| tree t0 = normalize_expression (TREE_OPERAND (t, 0)); |
| tree t1 = normalize_expression (TREE_OPERAND (t, 1)); |
| return build_nt (c, t0, t1); |
| } |
| |
| /* A simple requirement T introduces an expression constraint |
| for its expression. */ |
| |
| inline tree |
| normalize_simple_requirement (tree t) |
| { |
| return build_nt (EXPR_CONSTR, TREE_OPERAND (t, 0)); |
| } |
| |
| /* A type requirement T introduce a type constraint for its type. */ |
| |
| inline tree |
| normalize_type_requirement (tree t) |
| { |
| return build_nt (TYPE_CONSTR, TREE_OPERAND (t, 0)); |
| } |
| |
| /* A compound requirement T introduces a conjunction of constraints |
| depending on its form. The conjunction always includes an |
| expression constraint for the expression of the requirement. |
| If a trailing return type was specified, the conjunction includes |
| either an implicit conversion constraint or an argument deduction |
| constraint. If the noexcept specifier is present, the conjunction |
| includes an exception constraint. */ |
| |
| tree |
| normalize_compound_requirement (tree t) |
| { |
| tree expr = TREE_OPERAND (t, 0); |
| tree constr = build_nt (EXPR_CONSTR, TREE_OPERAND (t, 0)); |
| |
| /* If a type is given, append an implicit conversion or |
| argument deduction constraint. */ |
| if (tree type = TREE_OPERAND (t, 1)) |
| { |
| tree type_constr; |
| /* TODO: We should be extracting a list of auto nodes |
| from type_uses_auto, not a single node */ |
| if (tree placeholder = type_uses_auto (type)) |
| type_constr = build_nt (DEDUCT_CONSTR, expr, type, placeholder); |
| else |
| type_constr = build_nt (ICONV_CONSTR, expr, type); |
| constr = conjoin_constraints (constr, type_constr); |
| } |
| |
| /* If noexcept is present, append an exception constraint. */ |
| if (COMPOUND_REQ_NOEXCEPT_P (t)) |
| { |
| tree except = build_nt (EXCEPT_CONSTR, expr); |
| constr = conjoin_constraints (constr, except); |
| } |
| |
| return constr; |
| } |
| |
| /* A nested requirement T introduces a conjunction of constraints |
| corresponding to its constraint-expression. |
| |
| If the result of transforming T is error_mark_node, the resulting |
| constraint is a predicate constraint whose operand is also |
| error_mark_node. This preserves the constraint structure, but |
| will guarantee that the constraint is never satisfied. */ |
| |
| inline tree |
| normalize_nested_requirement (tree t) |
| { |
| return normalize_expression (TREE_OPERAND (t, 0)); |
| } |
| |
| /* Transform a requirement T into one or more constraints. */ |
| |
| tree |
| normalize_requirement (tree t) |
| { |
| switch (TREE_CODE (t)) |
| { |
| case SIMPLE_REQ: |
| return normalize_simple_requirement (t); |
| |
| case TYPE_REQ: |
| return normalize_type_requirement (t); |
| |
| case COMPOUND_REQ: |
| return normalize_compound_requirement (t); |
| |
| case NESTED_REQ: |
| return normalize_nested_requirement (t); |
| |
| default: |
| gcc_unreachable (); |
| } |
| return error_mark_node; |
| } |
| |
| /* Transform a sequence of requirements into a conjunction of |
| constraints. */ |
| |
| tree |
| normalize_requirements (tree t) |
| { |
| tree result = NULL_TREE; |
| for (; t; t = TREE_CHAIN (t)) |
| { |
| tree constr = normalize_requirement (TREE_VALUE (t)); |
| result = conjoin_constraints (result, constr); |
| } |
| return result; |
| } |
| |
| /* The normal form of a requires-expression is a parameterized |
| constraint having the same parameters and a conjunction of |
| constraints representing the normal form of requirements. */ |
| |
| tree |
| normalize_requires_expression (tree t) |
| { |
| tree operand = normalize_requirements (TREE_OPERAND (t, 1)); |
| if (tree parms = TREE_OPERAND (t, 0)) |
| return build_nt (PARM_CONSTR, parms, operand); |
| else |
| return operand; |
| } |
| |
| /* For a template-id referring to a variable concept, returns |
| a check constraint. Otherwise, returns a predicate constraint. */ |
| |
| tree |
| normalize_template_id_expression (tree t) |
| { |
| if (tree info = resolve_variable_concept_check (t)) |
| { |
| if (info == error_mark_node) |
| { |
| /* We get this when the template arguments don't match |
| the variable concept. */ |
| error ("invalid reference to concept %qE", t); |
| return error_mark_node; |
| } |
| |
| tree decl = TREE_VALUE (info); |
| tree args = TREE_PURPOSE (info); |
| return build_nt (CHECK_CONSTR, decl, args); |
| } |
| |
| /* Check that we didn't refer to a function concept like a variable. */ |
| tree fn = OVL_FIRST (TREE_OPERAND (t, 0)); |
| if (TREE_CODE (fn) == TEMPLATE_DECL |
| && DECL_DECLARED_CONCEPT_P (DECL_TEMPLATE_RESULT (fn))) |
| { |
| error_at (location_of (t), |
| "invalid reference to function concept %qD", fn); |
| return error_mark_node; |
| } |
| |
| return build_nt (PRED_CONSTR, t); |
| } |
| |
| /* For a call expression to a function concept, returns a check |
| constraint. Otherwise, returns a predicate constraint. */ |
| |
| tree |
| normalize_call_expression (tree t) |
| { |
| /* Try to resolve this function call as a concept. If not, then |
| it can be returned as a predicate constraint. */ |
| tree check = resolve_constraint_check (t); |
| if (!check) |
| return build_nt (PRED_CONSTR, t); |
| if (check == error_mark_node) |
| { |
| /* TODO: Improve diagnostics. We could report why the reference |
| is invalid. */ |
| error ("invalid reference to concept %qE", t); |
| return error_mark_node; |
| } |
| |
| tree fn = TREE_VALUE (check); |
| tree args = TREE_PURPOSE (check); |
| return build_nt (CHECK_CONSTR, fn, args); |
| } |
| |
| /* If T is a call to an overloaded && or || operator, diagnose that |
| as a non-SFINAEable error. Returns true if an error is emitted. |
| |
| TODO: It would be better to diagnose this at the point of definition, |
| if possible. Perhaps we should immediately do a first-pass normalization |
| of a concept definition to catch obvious non-dependent errors like |
| this. */ |
| |
| bool |
| check_for_logical_overloads (tree t) |
| { |
| if (TREE_CODE (t) != CALL_EXPR) |
| return false; |
| |
| tree fn = CALL_EXPR_FN (t); |
| |
| /* For member calls, try extracting the function from the |
| component ref. */ |
| if (TREE_CODE (fn) == COMPONENT_REF) |
| { |
| fn = TREE_OPERAND (fn, 1); |
| if (TREE_CODE (fn) == BASELINK) |
| fn = BASELINK_FUNCTIONS (fn); |
| } |
| |
| if (TREE_CODE (fn) != FUNCTION_DECL) |
| return false; |
| |
| if (DECL_OVERLOADED_OPERATOR_P (fn)) |
| { |
| location_t loc = cp_expr_loc_or_loc (t, input_location); |
| error_at (loc, "constraint %qE, uses overloaded operator", t); |
| return true; |
| } |
| |
| return false; |
| } |
| |
| /* 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. */ |
| |
| tree |
| normalize_atom (tree t) |
| { |
| /* We can get constraints pushed down through pack expansions, so |
| just return them. */ |
| if (constraint_p (t)) |
| return t; |
| |
| tree type = TREE_TYPE (t); |
| if (!type || type_unknown_p (t) || TREE_CODE (type) == TEMPLATE_TYPE_PARM) |
| ; |
| else if (!dependent_type_p (type)) |
| { |
| if (check_for_logical_overloads (t)) |
| return error_mark_node; |
| |
| type = cv_unqualified (type); |
| if (!same_type_p (type, boolean_type_node)) |
| { |
| error ("predicate constraint %q+E does not have type %<bool%>", t); |
| return error_mark_node; |
| } |
| } |
| |
| if (TREE_CODE (t) == TEMPLATE_ID_EXPR) |
| return normalize_template_id_expression (t); |
| if (TREE_CODE (t) == CALL_EXPR) |
| return normalize_call_expression (t); |
| return build_nt (PRED_CONSTR, t); |
| } |
| |
| /* Push down the pack expansion EXP into the leaves of the constraint PAT. */ |
| |
| tree |
| push_down_pack_expansion (tree exp, tree pat) |
| { |
| switch (TREE_CODE (pat)) |
| { |
| case CONJ_CONSTR: |
| case DISJ_CONSTR: |
| { |
| pat = copy_node (pat); |
| TREE_OPERAND (pat, 0) |
| = push_down_pack_expansion (exp, TREE_OPERAND (pat, 0)); |
| TREE_OPERAND (pat, 1) |
| = push_down_pack_expansion (exp, TREE_OPERAND (pat, 1)); |
| return pat; |
| } |
| default: |
| { |
| exp = copy_node (exp); |
| SET_PACK_EXPANSION_PATTERN (exp, pat); |
| return exp; |
| } |
| } |
| } |
| |
| /* Transform a pack expansion into a constraint. First we transform the |
| pattern of the pack expansion, then we push the pack expansion down into the |
| leaves of the constraint so that partial ordering will work. */ |
| |
| tree |
| normalize_pack_expansion (tree t) |
| { |
| tree pat = normalize_expression (PACK_EXPANSION_PATTERN (t)); |
| return push_down_pack_expansion (t, pat); |
| } |
| |
| /* Transform an expression into a constraint. */ |
| |
| tree |
| normalize_any_expression (tree t) |
| { |
| switch (TREE_CODE (t)) |
| { |
| case TRUTH_ANDIF_EXPR: |
| return normalize_logical_operation (t, CONJ_CONSTR); |
| |
| case TRUTH_ORIF_EXPR: |
| return normalize_logical_operation (t, DISJ_CONSTR); |
| |
| case REQUIRES_EXPR: |
| return normalize_requires_expression (t); |
| |
| case BIND_EXPR: |
| return normalize_expression (BIND_EXPR_BODY (t)); |
| |
| case EXPR_PACK_EXPANSION: |
| return normalize_pack_expansion (t); |
| |
| default: |
| /* All other constraints are atomic. */ |
| return normalize_atom (t); |
| } |
| } |
| |
| /* Transform a statement into an expression. */ |
| tree |
| normalize_any_statement (tree t) |
| { |
| switch (TREE_CODE (t)) |
| { |
| case RETURN_EXPR: |
| return normalize_expression (TREE_OPERAND (t, 0)); |
| default: |
| gcc_unreachable (); |
| } |
| return error_mark_node; |
| } |
| |
| /* Reduction rules for the declaration T. */ |
| |
| tree |
| normalize_any_declaration (tree t) |
| { |
| switch (TREE_CODE (t)) |
| { |
| case VAR_DECL: |
| return normalize_atom (t); |
| default: |
| gcc_unreachable (); |
| } |
| return error_mark_node; |
| } |
| |
| /* Returns the normal form of a constraint expression. */ |
| |
| tree |
| normalize_expression (tree t) |
| { |
| if (!t) |
| return NULL_TREE; |
| |
| if (t == error_mark_node) |
| return error_mark_node; |
| |
| switch (TREE_CODE_CLASS (TREE_CODE (t))) |
| { |
| case tcc_unary: |
| case tcc_binary: |
| case tcc_expression: |
| case tcc_vl_exp: |
| return normalize_any_expression (t); |
| |
| case tcc_statement: |
| return normalize_any_statement (t); |
| |
| case tcc_declaration: |
| return normalize_any_declaration (t); |
| |
| case tcc_exceptional: |
| case tcc_constant: |
| case tcc_reference: |
| case tcc_comparison: |
| /* These are all atomic predicate constraints. */ |
| return normalize_atom (t); |
| |
| default: |
| /* Unhandled node kind. */ |
| gcc_unreachable (); |
| } |
| return error_mark_node; |
| } |
| |
| |
| /*--------------------------------------------------------------------------- |
| Constraint normalization |
| ---------------------------------------------------------------------------*/ |
| |
| tree normalize_constraint (tree); |
| |
| /* The normal form of the disjunction T0 /\ T1 is the conjunction |
| of the normal form of T0 and the normal form of T1. */ |
| |
| inline tree |
| normalize_conjunction (tree t) |
| { |
| tree t0 = normalize_constraint (TREE_OPERAND (t, 0)); |
| tree t1 = normalize_constraint (TREE_OPERAND (t, 1)); |
| return build_nt (CONJ_CONSTR, t0, t1); |
| } |
| |
| /* The normal form of the disjunction T0 \/ T1 is the disjunction |
| of the normal form of T0 and the normal form of T1. */ |
| |
| inline tree |
| normalize_disjunction (tree t) |
| { |
| tree t0 = normalize_constraint (TREE_OPERAND (t, 0)); |
| tree t1 = normalize_constraint (TREE_OPERAND (t, 1)); |
| return build_nt (DISJ_CONSTR, t0, t1); |
| } |
| |
| /* A predicate constraint is normalized in two stages. First all |
| references specializations of concepts are replaced by their |
| substituted definitions. Then, the resulting expression is |
| transformed into a constraint by transforming && expressions |
| into conjunctions and || into disjunctions. */ |
| |
| tree |
| normalize_predicate_constraint (tree t) |
| { |
| ++processing_template_decl; |
| tree expr = PRED_CONSTR_EXPR (t); |
| tree constr = normalize_expression (expr); |
| --processing_template_decl; |
| return constr; |
| } |
| |
| /* The normal form of a parameterized constraint is the normal |
| form of its operand. */ |
| |
| tree |
| normalize_parameterized_constraint (tree t) |
| { |
| tree parms = PARM_CONSTR_PARMS (t); |
| tree operand = normalize_constraint (PARM_CONSTR_OPERAND (t)); |
| return build_nt (PARM_CONSTR, parms, operand); |
| } |
| |
| /* Normalize the constraint T by reducing it so that it is |
| comprised of only conjunctions and disjunctions of atomic |
| constraints. */ |
| |
| tree |
| normalize_constraint (tree t) |
| { |
| if (!t) |
| return NULL_TREE; |
| |
| if (t == error_mark_node) |
| return t; |
| |
| switch (TREE_CODE (t)) |
| { |
| case CONJ_CONSTR: |
| return normalize_conjunction (t); |
| |
| case DISJ_CONSTR: |
| return normalize_disjunction (t); |
| |
| case PRED_CONSTR: |
| return normalize_predicate_constraint (t); |
| |
| case PARM_CONSTR: |
| return normalize_parameterized_constraint (t); |
| |
| case EXPR_CONSTR: |
| case TYPE_CONSTR: |
| case ICONV_CONSTR: |
| case DEDUCT_CONSTR: |
| case EXCEPT_CONSTR: |
| /* These constraints are defined to be atomic. */ |
| return t; |
| |
| default: |
| /* CONSTR was not a constraint. */ |
| gcc_unreachable(); |
| } |
| return error_mark_node; |
| } |
| |
| |
| |
| // -------------------------------------------------------------------------- // |
| // 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_PARM_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 || TREE_CODE (type) != RECORD_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 (!equivalent_constraints (ci, orig_ci)) |
| { |
| // FIXME: Improve diagnostics. |
| error ("%qT does not match any declaration", type); |
| return error_mark_node; |
| } |
| return type; |
| } |
| set_constraints (decl, ci); |
| } |
| return type; |
| } |
| |
| namespace { |
| |
| // Create an empty constraint info block. |
| inline tree_constraint_info* |
| build_constraint_info () |
| { |
| return (tree_constraint_info *)make_node (CONSTRAINT_INFO); |
| } |
| |
| } // namespace |
| |
| /* 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 tmpl_reqs, tree decl_reqs) |
| { |
| gcc_assert (tmpl_reqs ? constraint_p (tmpl_reqs) : true); |
| gcc_assert (decl_reqs ? constraint_p (decl_reqs) : true); |
| |
| if (!tmpl_reqs && !decl_reqs) |
| return NULL_TREE; |
| |
| tree_constraint_info* ci = build_constraint_info (); |
| ci->template_reqs = tmpl_reqs; |
| ci->declarator_reqs = decl_reqs; |
| ci->associated_constr = conjoin_constraints (tmpl_reqs, decl_reqs); |
| |
| return (tree)ci; |
| } |
| |
| namespace { |
| |
| /* Construct a sequence of template arguments by prepending |
| ARG to REST. Either ARG or REST may be null. */ |
| 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 |
| { |
| gcc_assert (rest != NULL_TREE); |
| args = rest; |
| } |
| return args; |
| } |
| |
| } // namespace |
| |
| /* Construct an expression that checks the concept given by |
| TARGET. The TARGET must be: |
| |
| - an OVERLOAD referring to one or more function concepts |
| - a BASELINK referring to an overload set of the above, or |
| - a TEMPLTATE_DECL referring to a variable concept. |
| |
| ARG and REST are the explicit template arguments for the |
| eventual concept check. */ |
| tree |
| build_concept_check (tree target, tree arg, tree rest) |
| { |
| tree args = build_concept_check_arguments (arg, rest); |
| if (variable_template_p (target)) |
| return build_variable_check (lookup_template_variable (target, args)); |
| else |
| return build_call_check (lookup_template_function (target, args)); |
| } |
| |
| |
| /* 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). |
| |
| This will always return a CHECK_CONSTR. */ |
| 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); |
| |
| /* If the parameter declaration is variadic, but the concept |
| is not then we need to apply the concept to every element |
| in the pack. */ |
| bool is_proto_pack = template_parameter_pack_p (proto); |
| bool is_decl_pack = template_parameter_pack_p (decl); |
| bool apply_to_all_p = is_decl_pack && !is_proto_pack; |
| |
| /* 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 (build_tree_list (NULL_TREE, decl)); |
| if (apply_to_all_p) |
| arg = PACK_EXPANSION_PATTERN (TREE_VEC_ELT (ARGUMENT_PACK_ARGS (arg), 0)); |
| |
| /* Build the concept check. If it the constraint needs to be |
| applied to all elements of the parameter pack, then make |
| the constraint an expansion. */ |
| tree tmpl = DECL_TI_TEMPLATE (con); |
| tree check = VAR_P (con) ? tmpl : ovl_make (tmpl); |
| check = build_concept_check (check, arg, args); |
| |
| /* Make the check a pack expansion if needed. |
| |
| FIXME: We should be making a fold expression. */ |
| if (apply_to_all_p) |
| { |
| check = make_pack_expansion (check); |
| TREE_TYPE (check) = boolean_type_node; |
| } |
| |
| return normalize_expression (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 = conjoin_constraints (result, constr); |
| } |
| return result; |
| } |
| |
| // Returns and chains a new parameter for PARAMETER_LIST which will conform |
| // to the prototype given by SRC_PARM. The new parameter will have its |
| // identifier and location set according to IDENT and PARM_LOC respectively. |
| static tree |
| process_introduction_parm (tree parameter_list, tree src_parm) |
| { |
| // If we have a pack, we should have a single pack argument which is the |
| // placeholder we want to look at. |
| bool is_parameter_pack = ARGUMENT_PACK_P (src_parm); |
| if (is_parameter_pack) |
| src_parm = TREE_VEC_ELT (ARGUMENT_PACK_ARGS (src_parm), 0); |
| |
| // At this point we should have a wildcard, but we want to |
| // grab the associated decl from it. Also grab the stored |
| // identifier and location that should be chained to it in |
| // a PARM_DECL. |
| gcc_assert (TREE_CODE (src_parm) == WILDCARD_DECL); |
| |
| tree ident = DECL_NAME (src_parm); |
| location_t parm_loc = DECL_SOURCE_LOCATION (src_parm); |
| |
| // If we expect a pack and the deduced template is not a pack, or if the |
| // template is using a pack and we didn't declare a pack, throw an error. |
| if (is_parameter_pack != WILDCARD_PACK_P (src_parm)) |
| { |
| error_at (parm_loc, "cannot match pack for introduced parameter"); |
| tree err_parm = build_tree_list (error_mark_node, error_mark_node); |
| return chainon (parameter_list, err_parm); |
| } |
| |
| src_parm = TREE_TYPE (src_parm); |
| |
| tree parm; |
| bool is_non_type; |
| if (TREE_CODE (src_parm) == TYPE_DECL) |
| { |
| is_non_type = false; |
| parm = finish_template_type_parm (class_type_node, ident); |
| } |
| else if (TREE_CODE (src_parm) == TEMPLATE_DECL) |
| { |
| is_non_type = false; |
| begin_template_parm_list (); |
| current_template_parms = DECL_TEMPLATE_PARMS (src_parm); |
| end_template_parm_list (); |
| parm = finish_template_template_parm (class_type_node, ident); |
| } |
| else |
| { |
| is_non_type = true; |
| |
| // Since we don't have a declarator, so we can copy the source |
| // parameter and change the name and eventually the location. |
| parm = copy_decl (src_parm); |
| DECL_NAME (parm) = ident; |
| } |
| |
| // Wrap in a TREE_LIST for process_template_parm. Introductions do not |
| // retain the defaults from the source template. |
| parm = build_tree_list (NULL_TREE, parm); |
| |
| return process_template_parm (parameter_list, parm_loc, parm, |
| is_non_type, is_parameter_pack); |
| } |
| |
| /* 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) |
| { |
| /* Deduce the concept check. */ |
| tree expr = build_concept_check (tmpl_decl, NULL_TREE, intro_list); |
| if (expr == error_mark_node) |
| return NULL_TREE; |
| |
| 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; ++n) |
| parm_list = process_introduction_parm (parm_list, TREE_VEC_ELT (parms, n)); |
| parm_list = end_template_parm_list (parm_list); |
| 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 (TREE_VEC_LENGTH (parms)); |
| 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, NULL_TREE, check_args); |
| tree constr = normalize_expression (check); |
| TEMPLATE_PARMS_CONSTRAINTS (current_template_parms) = constr; |
| |
| return parm_list; |
| } |
| |
| |
| /* Given the predicate constraint 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 (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; |
| } |
| |
| if (TREE_CODE (t) == CHECK_CONSTR) |
| { |
| tree decl = CHECK_CONSTR_CONCEPT (t); |
| tmpl = DECL_TI_TEMPLATE (decl); |
| args = CHECK_CONSTR_ARGS (t); |
| return; |
| } |
| |
| gcc_unreachable (); |
| } |
| |
| /* Returns true iff the placeholders C1 and C2 are equivalent. C1 |
| and C2 can be either CHECK_CONSTR or TEMPLATE_TYPE_PARM. */ |
| |
| 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 PRED_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; |
| } |
| |
| /*--------------------------------------------------------------------------- |
| Constraint substitution |
| ---------------------------------------------------------------------------*/ |
| |
| /* The following functions implement substitution rules for constraints. |
| Substitution without checking constraints happens only in the |
| instantiation of class templates. For example: |
| |
| template<C1 T> struct S { |
| void f(T) requires C2<T>; |
| void g(T) requires T::value; |
| }; |
| |
| S<int> s; // error instantiating S<int>::g(T) |
| |
| When we instantiate S, we substitute into its member declarations, |
| including their constraints. However, those constraints are not |
| checked. Substituting int into C2<T> yields C2<int>, and substituting |
| into T::value yields a substitution failure, making the program |
| ill-formed. |
| |
| Note that we only ever substitute into the associated constraints |
| of a declaration. That is, substitution is defined only for predicate |
| constraints and conjunctions. */ |
| |
| /* Substitute into the predicate constraints. Returns error_mark_node |
| if the substitution into the expression fails. */ |
| tree |
| tsubst_predicate_constraint (tree t, tree args, |
| tsubst_flags_t complain, tree in_decl) |
| { |
| tree expr = PRED_CONSTR_EXPR (t); |
| ++processing_template_decl; |
| tree result = tsubst_expr (expr, args, complain, in_decl, false); |
| --processing_template_decl; |
| return build_nt (PRED_CONSTR, result); |
| } |
| |
| /* Substitute into a check constraint. */ |
| |
| tree |
| tsubst_check_constraint (tree t, tree args, |
| tsubst_flags_t complain, tree in_decl) |
| { |
| tree decl = CHECK_CONSTR_CONCEPT (t); |
| tree tmpl = DECL_TI_TEMPLATE (decl); |
| tree targs = CHECK_CONSTR_ARGS (t); |
| |
| /* Substitute through by building an template-id expression |
| and then substituting into that. */ |
| tree expr = build_nt (TEMPLATE_ID_EXPR, tmpl, targs); |
| ++processing_template_decl; |
| tree result = tsubst_expr (expr, args, complain, in_decl, false); |
| --processing_template_decl; |
| |
| if (result == error_mark_node) |
| return error_mark_node; |
| |
| /* Extract the results and rebuild the check constraint. */ |
| decl = DECL_TEMPLATE_RESULT (TREE_OPERAND (result, 0)); |
| args = TREE_OPERAND (result, 1); |
| |
| return build_nt (CHECK_CONSTR, decl, args); |
| } |
| |
| /* Substitute into the conjunction of constraints. Returns |
| error_mark_node if substitution into either operand fails. */ |
| |
| tree |
| tsubst_logical_operator (tree t, tree args, |
| tsubst_flags_t complain, tree in_decl) |
| { |
| tree t0 = TREE_OPERAND (t, 0); |
| tree r0 = tsubst_constraint (t0, args, complain, in_decl); |
| if (r0 == error_mark_node) |
| return error_mark_node; |
| tree t1 = TREE_OPERAND (t, 1); |
| tree r1 = tsubst_constraint (t1, args, complain, in_decl); |
| if (r1 == error_mark_node) |
| return error_mark_node; |
| return build_nt (TREE_CODE (t), r0, r1); |
| } |
| |
| namespace { |
| |
| /* Substitute ARGS into the expression constraint T. */ |
| |
| tree |
| tsubst_expr_constr (tree t, tree args, tsubst_flags_t complain, tree in_decl) |
| { |
| cp_unevaluated guard; |
| tree expr = EXPR_CONSTR_EXPR (t); |
| tree ret = tsubst_expr (expr, args, complain, in_decl, false); |
| if (ret == error_mark_node) |
| return error_mark_node; |
| return build_nt (EXPR_CONSTR, ret); |
| } |
| |
| /* Substitute ARGS into the type constraint T. */ |
| |
| tree |
| tsubst_type_constr (tree t, tree args, tsubst_flags_t complain, tree in_decl) |
| { |
| tree type = TYPE_CONSTR_TYPE (t); |
| tree ret = tsubst (type, args, complain, in_decl); |
| if (ret == error_mark_node) |
| return error_mark_node; |
| return build_nt (TYPE_CONSTR, ret); |
| } |
| |
| /* Substitute ARGS into the implicit conversion constraint T. */ |
| |
| tree |
| tsubst_implicit_conversion_constr (tree t, tree args, tsubst_flags_t complain, |
| tree in_decl) |
| { |
| cp_unevaluated guard; |
| tree expr = ICONV_CONSTR_EXPR (t); |
| tree type = ICONV_CONSTR_TYPE (t); |
| tree new_expr = tsubst_expr (expr, args, complain, in_decl, false); |
| if (new_expr == error_mark_node) |
| return error_mark_node; |
| tree new_type = tsubst (type, args, complain, in_decl); |
| if (new_type == error_mark_node) |
| return error_mark_node; |
| return build_nt (ICONV_CONSTR, new_expr, new_type); |
| } |
| |
| /* Substitute ARGS into the argument deduction constraint T. */ |
| |
| tree |
| tsubst_argument_deduction_constr (tree t, tree args, tsubst_flags_t complain, |
| tree in_decl) |
| { |
| cp_unevaluated guard; |
| tree expr = DEDUCT_CONSTR_EXPR (t); |
| tree pattern = DEDUCT_CONSTR_PATTERN (t); |
| tree autos = DEDUCT_CONSTR_PLACEHOLDER(t); |
| tree new_expr = tsubst_expr (expr, args, complain, in_decl, false); |
| if (new_expr == error_mark_node) |
| return error_mark_node; |
| /* It seems like substituting through the pattern will not affect the |
| placeholders. We should (?) be able to reuse the existing list |
| without any problems. If not, then we probably want to create a |
| new list of placeholders and then instantiate the pattern using |
| those. */ |
| tree new_pattern = tsubst (pattern, args, complain, in_decl); |
| if (new_pattern == error_mark_node) |
| return error_mark_node; |
| return build_nt (DEDUCT_CONSTR, new_expr, new_pattern, autos); |
| } |
| |
| /* Substitute ARGS into the exception constraint T. */ |
| |
| tree |
| tsubst_exception_constr (tree t, tree args, tsubst_flags_t complain, |
| tree in_decl) |
| { |
| cp_unevaluated guard; |
| tree expr = EXCEPT_CONSTR_EXPR (t); |
| tree ret = tsubst_expr (expr, args, complain, in_decl, false); |
| if (ret == error_mark_node) |
| return error_mark_node; |
| return build_nt (EXCEPT_CONSTR, ret); |
| } |
| |
| /* A subroutine of tsubst_constraint_variables. Register local |
| specializations for each of parameter in PARMS and its |
| corresponding substituted constraint variable in VARS. |
| Returns VARS. */ |
| |
| 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; |
| } |
| |
| /* 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. */ |
| |
| tree |
| tsubst_constraint_variables (tree t, tree args, |
| tsubst_flags_t complain, tree in_decl) |
| { |
| /* 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, complain, 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 parameterized constraint T. */ |
| |
| tree |
| tsubst_parameterized_constraint (tree t, tree args, |
| tsubst_flags_t complain, tree in_decl) |
| { |
| local_specialization_stack stack; |
| tree vars = tsubst_constraint_variables (PARM_CONSTR_PARMS (t), |
| args, complain, in_decl); |
| if (vars == error_mark_node) |
| return error_mark_node; |
| tree expr = tsubst_constraint (PARM_CONSTR_OPERAND (t), args, |
| complain, in_decl); |
| if (expr == error_mark_node) |
| return error_mark_node; |
| return build_nt (PARM_CONSTR, vars, expr); |
| } |
| |
| /* Substitute ARGS into the simple requirement T. Note that |
| substitution may result in an ill-formed expression without |
| causing the program to be ill-formed. In such cases, the |
| requirement wraps an error_mark_node. */ |
| |
| inline tree |
| tsubst_simple_requirement (tree t, tree args, |
| tsubst_flags_t complain, tree in_decl) |
| { |
| ++processing_template_decl; |
| tree expr = tsubst_expr (TREE_OPERAND (t, 0), args, complain, in_decl, false); |
| --processing_template_decl; |
| return finish_simple_requirement (expr); |
| } |
| |
| /* Substitute ARGS into the type requirement T. Note that |
| substitution may result in an ill-formed type without |
| causing the program to be ill-formed. In such cases, the |
| requirement wraps an error_mark_node. */ |
| |
| inline tree |
| tsubst_type_requirement (tree t, tree args, |
| tsubst_flags_t complain, tree in_decl) |
| { |
| ++processing_template_decl; |
| tree type = tsubst (TREE_OPERAND (t, 0), args, complain, in_decl); |
| --processing_template_decl; |
| return finish_type_requirement (type); |
| } |
| |
| /* Substitute args into the compound requirement T. If substituting |
| into either the expression or the type fails, the corresponding |
| operands in the resulting node will be error_mark_node. This |
| preserves a requirement for the purpose of partial ordering, but |
| it will never be satisfied. */ |
| |
| tree |
| tsubst_compound_requirement (tree t, tree args, |
| tsubst_flags_t complain, tree in_decl) |
| { |
| ++processing_template_decl; |
| tree expr = tsubst_expr (TREE_OPERAND (t, 0), args, complain, in_decl, false); |
| tree type = tsubst (TREE_OPERAND (t, 1), args, complain, in_decl); |
| --processing_template_decl; |
| bool noexcept_p = COMPOUND_REQ_NOEXCEPT_P (t); |
| return finish_compound_requirement (expr, type, noexcept_p); |
| } |
| |
| /* Substitute ARGS into the nested requirement T. */ |
| |
| tree |
| tsubst_nested_requirement (tree t, tree args, |
| tsubst_flags_t complain, tree in_decl) |
| { |
| ++processing_template_decl; |
| tree expr = tsubst_expr (TREE_OPERAND (t, 0), args, complain, in_decl, false); |
| --processing_template_decl; |
| return finish_nested_requirement (expr); |
| } |
| |
| /* Substitute ARGS into the requirement T. */ |
| |
| inline tree |
| tsubst_requirement (tree t, tree args, tsubst_flags_t complain, tree in_decl) |
| { |
| switch (TREE_CODE (t)) |
| { |
| case SIMPLE_REQ: |
| return tsubst_simple_requirement (t, args, complain, in_decl); |
| case TYPE_REQ: |
| return tsubst_type_requirement (t, args, complain, in_decl); |
| case COMPOUND_REQ: |
| return tsubst_compound_requirement (t, args, complain, in_decl); |
| case NESTED_REQ: |
| return tsubst_nested_requirement (t, args, complain, in_decl); |
| default: |
| gcc_unreachable (); |
| } |
| return error_mark_node; |
| } |
| |
| /* Substitute ARGS into the list of requirements T. Note that |
| substitution failures here result in ill-formed programs. */ |
| |
| tree |
| tsubst_requirement_body (tree t, tree args, |
| tsubst_flags_t complain, tree in_decl) |
| { |
| tree r = NULL_TREE; |
| while (t) |
| { |
| tree e = tsubst_requirement (TREE_VALUE (t), args, complain, in_decl); |
| if (e == error_mark_node) |
| return error_mark_node; |
| r = tree_cons (NULL_TREE, e, r); |
| t = TREE_CHAIN (t); |
| } |
| /* Ensure that the order of constraints is the same as the original. */ |
| return nreverse (r); |
| } |
| |
| } /* namespace */ |
| |
| /* Substitute ARGS into the requires expression T. Note that this |
| results in the re-declaration of local parameters when |
| substituting through the parameter list. If either substitution |
| fails, the program is ill-formed. */ |
| |
| tree |
| tsubst_requires_expr (tree t, tree args, |
| tsubst_flags_t complain, tree in_decl) |
| { |
| local_specialization_stack stack; |
| |
| tree parms = TREE_OPERAND (t, 0); |
| if (parms) |
| { |
| parms = tsubst_constraint_variables (parms, args, complain, in_decl); |
| if (parms == error_mark_node) |
| return error_mark_node; |
| } |
| |
| tree reqs = TREE_OPERAND (t, 1); |
| reqs = tsubst_requirement_body (reqs, args, complain, in_decl); |
| if (reqs == error_mark_node) |
| return error_mark_node; |
| |
| return finish_requires_expr (parms, reqs); |
| } |
| |
| /* 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 tmpl_constr = NULL_TREE; |
| if (tree r = CI_TEMPLATE_REQS (t)) |
| tmpl_constr = tsubst_constraint (r, args, complain, in_decl); |
| |
| tree decl_constr = NULL_TREE; |
| if (tree r = CI_DECLARATOR_REQS (t)) |
| decl_constr = tsubst_constraint (r, args, complain, in_decl); |
| |
| return build_constraints (tmpl_constr, decl_constr); |
| } |
| |
| /* Substitute ARGS into the constraint T. */ |
| |
| tree |
| tsubst_constraint (tree t, tree args, tsubst_flags_t complain, tree in_decl) |
| { |
| if (t == NULL_TREE || t == error_mark_node) |
| return t; |
| switch (TREE_CODE (t)) |
| { |
| case PRED_CONSTR: |
| return tsubst_predicate_constraint (t, args, complain, in_decl); |
| case CHECK_CONSTR: |
| return tsubst_check_constraint (t, args, complain, in_decl); |
| case CONJ_CONSTR: |
| case DISJ_CONSTR: |
| return tsubst_logical_operator (t, args, complain, in_decl); |
| case PARM_CONSTR: |
| return tsubst_parameterized_constraint (t, args, complain, in_decl); |
| case EXPR_CONSTR: |
| return tsubst_expr_constr (t, args, complain, in_decl); |
| case TYPE_CONSTR: |
| return tsubst_type_constr (t, args, complain, in_decl); |
| case ICONV_CONSTR: |
| return tsubst_implicit_conversion_constr (t, args, complain, in_decl); |
| case DEDUCT_CONSTR: |
| return tsubst_argument_deduction_constr (t, args, complain, in_decl); |
| case EXCEPT_CONSTR: |
| return tsubst_exception_constr (t, args, complain, in_decl); |
| default: |
| gcc_unreachable (); |
| } |
| return error_mark_node; |
| } |
| |
| /*--------------------------------------------------------------------------- |
| Constraint satisfaction |
| ---------------------------------------------------------------------------*/ |
| |
| /* The following functions determine if a constraint, when |
| substituting template arguments, is satisfied. For convenience, |
| satisfaction reduces a constraint to either true or false (and |
| nothing else). */ |
| |
| namespace { |
| |
| tree satisfy_constraint_1 (tree, tree, tsubst_flags_t, tree); |
| |
| /* Check the constraint pack expansion. */ |
| |
| tree |
| satisfy_pack_expansion (tree t, tree args, |
| tsubst_flags_t complain, tree in_decl) |
| { |
| /* Get the vector of satisfaction results. |
| gen_elem_of_pack_expansion_instantiation will check that each element of |
| the expansion is satisfied. */ |
| tree exprs = tsubst_pack_expansion (t, args, complain, in_decl); |
| |
| if (exprs == error_mark_node) |
| return boolean_false_node; |
| |
| /* TODO: It might be better to normalize each expanded term |
| and evaluate them separately. That would provide better |
| opportunities for diagnostics. */ |
| for (int i = 0; i < TREE_VEC_LENGTH (exprs); ++i) |
| if (TREE_VEC_ELT (exprs, i) != boolean_true_node) |
| return boolean_false_node; |
| return boolean_true_node; |
| } |
| |
| /* A predicate constraint is satisfied if its expression evaluates |
| to true. If substitution into that node fails, the constraint |
| is not satisfied ([temp.constr.pred]). |
| |
| Note that a predicate constraint is a constraint expression |
| of type bool. If neither of those are true, the program is |
| ill-formed; they are not SFINAE'able errors. */ |
| |
| tree |
| satisfy_predicate_constraint (tree t, tree args, |
| tsubst_flags_t complain, tree in_decl) |
| { |
| tree expr = TREE_OPERAND (t, 0); |
| |
| /* We should never have a naked pack expansion in a predicate constraint. */ |
| gcc_assert (TREE_CODE (expr) != EXPR_PACK_EXPANSION); |
| |
| /* If substitution into the expression fails, the constraint |
| is not satisfied. */ |
| expr = tsubst_expr (expr, args, complain, in_decl, false); |
| if (expr == error_mark_node) |
| return boolean_false_node; |
| |
| /* A predicate constraint shall have type bool. In some |
| cases, substitution gives us const-qualified bool, which |
| is also acceptable. */ |
| tree type = cv_unqualified (TREE_TYPE (expr)); |
| if (!same_type_p (type, boolean_type_node)) |
| { |
| error_at (cp_expr_loc_or_loc (expr, input_location), |
| "constraint %qE does not have type %qT", |
| expr, boolean_type_node); |
| return boolean_false_node; |
| } |
| |
| return cxx_constant_value (expr); |
| } |
| |
| /* A concept check constraint like C<CARGS> is satisfied if substituting ARGS |
| into CARGS succeeds and C is satisfied for the resulting arguments. */ |
| |
| tree |
| satisfy_check_constraint (tree t, tree args, |
| tsubst_flags_t complain, tree in_decl) |
| { |
| tree decl = CHECK_CONSTR_CONCEPT (t); |
| tree tmpl = DECL_TI_TEMPLATE (decl); |
| tree cargs = CHECK_CONSTR_ARGS (t); |
| |
| /* Instantiate the concept check arguments. */ |
| tree targs = tsubst (cargs, args, tf_none, NULL_TREE); |
| if (targs == error_mark_node) |
| return boolean_false_node; |
| |
| /* Search for a previous value. */ |
| if (tree prev = lookup_concept_satisfaction (tmpl, targs)) |
| return prev; |
| |
| /* Expand the concept; failure here implies non-satisfaction. */ |
| tree def = expand_concept (decl, targs); |
| if (def == error_mark_node) |
| return memoize_concept_satisfaction (tmpl, args, boolean_false_node); |
| |
| /* Recursively satisfy the constraint. */ |
| tree result = satisfy_constraint_1 (def, targs, complain, in_decl); |
| return memoize_concept_satisfaction (tmpl, targs, result); |
| } |
| |
| /* Check an expression constraint. The constraint is satisfied if |
| substitution succeeds ([temp.constr.expr]). |
| |
| Note that the expression is unevaluated. */ |
| |
| tree |
| satisfy_expression_constraint (tree t, tree args, |
| tsubst_flags_t complain, tree in_decl) |
| { |
| cp_unevaluated guard; |
| deferring_access_check_sentinel deferring; |
| |
| tree expr = EXPR_CONSTR_EXPR (t); |
| tree check = tsubst_expr (expr, args, complain, in_decl, false); |
| if (check == error_mark_node) |
| return boolean_false_node; |
| if (!perform_deferred_access_checks (tf_none)) |
| return boolean_false_node; |
| return boolean_true_node; |
| } |
| |
| /* Check a type constraint. The constraint is satisfied if |
| substitution succeeds. */ |
| |
| inline tree |
| satisfy_type_constraint (tree t, tree args, |
| tsubst_flags_t complain, tree in_decl) |
| { |
| deferring_access_check_sentinel deferring; |
| tree type = TYPE_CONSTR_TYPE (t); |
| gcc_assert (TYPE_P (type) || type == error_mark_node); |
| tree check = tsubst (type, args, complain, in_decl); |
| if (error_operand_p (check)) |
| return boolean_false_node; |
| if (!perform_deferred_access_checks (complain)) |
| return boolean_false_node; |
| return boolean_true_node; |
| } |
| |
| /* Check an implicit conversion constraint. */ |
| |
| tree |
| satisfy_implicit_conversion_constraint (tree t, tree args, |
| tsubst_flags_t complain, tree in_decl) |
| { |
| /* Don't tsubst as if we're processing a template. If we try |
| to we can end up generating template-like expressions |
| (e.g., modop-exprs) that aren't properly typed. */ |
| tree expr = |
| tsubst_expr (ICONV_CONSTR_EXPR (t), args, complain, in_decl, false); |
| if (expr == error_mark_node) |
| return boolean_false_node; |
| |
| /* Get the transformed target type. */ |
| tree type = tsubst (ICONV_CONSTR_TYPE (t), args, complain, in_decl); |
| if (type == error_mark_node) |
| return boolean_false_node; |
| |
| /* Attempt the conversion as a direct initialization |
| of the form TYPE <unspecified> = EXPR. */ |
| tree conv = |
| perform_direct_initialization_if_possible (type, expr, false, complain); |
| if (conv == NULL_TREE || conv == error_mark_node) |
| return boolean_false_node; |
| else |
| return boolean_true_node; |
| } |
| |
| /* Check an argument deduction constraint. */ |
| |
| tree |
| satisfy_argument_deduction_constraint (tree t, tree args, |
| tsubst_flags_t complain, tree in_decl) |
| { |
| /* Substitute through the expression. */ |
| tree expr = DEDUCT_CONSTR_EXPR (t); |
| tree init = tsubst_expr (expr, args, complain, in_decl, false); |
| if (expr == error_mark_node) |
| return boolean_false_node; |
| |
| /* Perform auto or decltype(auto) deduction to get the result. */ |
| tree pattern = DEDUCT_CONSTR_PATTERN (t); |
| tree placeholder = DEDUCT_CONSTR_PLACEHOLDER (t); |
| tree constr = PLACEHOLDER_TYPE_CONSTRAINTS (placeholder); |
| tree type_canonical = TYPE_CANONICAL (placeholder); |
| PLACEHOLDER_TYPE_CONSTRAINTS (placeholder) |
| = tsubst_constraint (constr, args, complain|tf_partial, in_decl); |
| TYPE_CANONICAL (placeholder) = NULL_TREE; |
| tree type = do_auto_deduction (pattern, init, placeholder, |
| complain, adc_requirement); |
| PLACEHOLDER_TYPE_CONSTRAINTS (placeholder) = constr; |
| TYPE_CANONICAL (placeholder) = type_canonical; |
| if (type == error_mark_node) |
| return boolean_false_node; |
| |
| return boolean_true_node; |
| } |
| |
| /* Check an exception constraint. An exception constraint for an |
| expression e is satisfied when noexcept(e) is true. */ |
| |
| tree |
| satisfy_exception_constraint (tree t, tree args, |
| tsubst_flags_t complain, tree in_decl) |
| { |
| tree expr = EXCEPT_CONSTR_EXPR (t); |
| tree check = tsubst_expr (expr, args, complain, in_decl, false); |
| if (check == error_mark_node) |
| return boolean_false_node; |
| |
| if (expr_noexcept_p (check, complain)) |
| return boolean_true_node; |
| else |
| return boolean_false_node; |
| } |
| |
| /* Check a parameterized constraint. */ |
| |
| tree |
| satisfy_parameterized_constraint (tree t, tree args, |
| tsubst_flags_t complain, tree in_decl) |
| { |
| local_specialization_stack stack; |
| tree parms = PARM_CONSTR_PARMS (t); |
| tree vars = tsubst_constraint_variables (parms, args, complain, in_decl); |
| if (vars == error_mark_node) |
| return boolean_false_node; |
| tree constr = PARM_CONSTR_OPERAND (t); |
| return satisfy_constraint_1 (constr, args, complain, in_decl); |
| } |
| |
| /* Check that the conjunction of constraints is satisfied. Note |
| that if left operand is not satisfied, the right operand |
| is not checked. |
| |
| FIXME: Check that this wouldn't result in a user-defined |
| operator. Note that this error is partially diagnosed in |
| satisfy_predicate_constraint. It would be nice to diagnose |
| the overload, but I don't think it's strictly necessary. */ |
| |
| tree |
| satisfy_conjunction (tree t, tree args, tsubst_flags_t complain, tree in_decl) |
| { |
| tree t0 = satisfy_constraint_1 (TREE_OPERAND (t, 0), args, complain, in_decl); |
| if (t0 == boolean_false_node) |
| return boolean_false_node; |
| return satisfy_constraint_1 (TREE_OPERAND (t, 1), args, complain, in_decl); |
| } |
| |
| /* Check that the disjunction of constraints is satisfied. Note |
| that if the left operand is satisfied, the right operand is not |
| checked. */ |
| |
| tree |
| satisfy_disjunction (tree t, tree args, tsubst_flags_t complain, tree in_decl) |
| { |
| tree t0 = satisfy_constraint_1 (TREE_OPERAND (t, 0), args, complain, in_decl); |
| if (t0 == boolean_true_node) |
| return boolean_true_node; |
| return satisfy_constraint_1 (TREE_OPERAND (t, 1), args, complain, in_decl); |
| } |
| |
| /* Dispatch to an appropriate satisfaction routine depending on the |
| tree code of T. */ |
| |
| tree |
| satisfy_constraint_1 (tree t, tree args, tsubst_flags_t complain, tree in_decl) |
| { |
| gcc_assert (!processing_template_decl); |
| |
| if (!t) |
| return boolean_false_node; |
| |
| if (t == error_mark_node) |
| return boolean_false_node; |
| |
| switch (TREE_CODE (t)) |
| { |
| case PRED_CONSTR: |
| return satisfy_predicate_constraint (t, args, complain, in_decl); |
| |
| case CHECK_CONSTR: |
| return satisfy_check_constraint (t, args, complain, in_decl); |
| |
| case EXPR_CONSTR: |
| return satisfy_expression_constraint (t, args, complain, in_decl); |
| |
| case TYPE_CONSTR: |
| return satisfy_type_constraint (t, args, complain, in_decl); |
| |
| case ICONV_CONSTR: |
| return satisfy_implicit_conversion_constraint (t, args, complain, in_decl); |
| |
| case DEDUCT_CONSTR: |
| return satisfy_argument_deduction_constraint (t, args, complain, in_decl); |
| |
| case EXCEPT_CONSTR: |
| return satisfy_exception_constraint (t, args, complain, in_decl); |
| |
| case PARM_CONSTR: |
| return satisfy_parameterized_constraint (t, args, complain, in_decl); |
| |
| case CONJ_CONSTR: |
| return satisfy_conjunction (t, args, complain, in_decl); |
| |
| case DISJ_CONSTR: |
| return satisfy_disjunction (t, args, complain, in_decl); |
| |
| case EXPR_PACK_EXPANSION: |
| return satisfy_pack_expansion (t, args, complain, in_decl); |
| |
| default: |
| gcc_unreachable (); |
| } |
| return boolean_false_node; |
| } |
| |
| /* Check that the constraint is satisfied, according to the rules |
| for that constraint. Note that each satisfy_* function returns |
| true or false, depending on whether it is satisfied or not. */ |
| |
| tree |
| satisfy_constraint (tree t, tree args) |
| { |
| auto_timevar time (TV_CONSTRAINT_SAT); |
| |
| /* 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); |
| |
| /* Avoid early exit in tsubst and tsubst_copy from null args; since earlier |
| substitution was done with processing_template_decl forced on, there will |
| be expressions that still need semantic processing, possibly buried in |
| decltype or a template argument. */ |
| if (args == NULL_TREE) |
| args = make_tree_vec (1); |
| |
| return satisfy_constraint_1 (t, args, tf_none, NULL_TREE); |
| } |
| |
| /* Check the associated constraints in CI against the given |
| ARGS, returning true when the constraints are satisfied |
| and false otherwise. */ |
| |
| tree |
| satisfy_associated_constraints (tree ci, tree args) |
| { |
| /* If there are no constraints then this is trivially satisfied. */ |
| if (!ci) |
| return boolean_true_node; |
| |
| /* If any arguments depend on template parameters, we can't |
| check constraints. */ |
| if (args && uses_template_parms (args)) |
| return boolean_true_node; |
| |
| /* Check if we've seen a previous result. */ |
| if (tree prev = lookup_constraint_satisfaction (ci, args)) |
| return prev; |
| |
| /* Actually test for satisfaction. */ |
| tree result = satisfy_constraint (CI_ASSOCIATED_CONSTRAINTS (ci), args); |
| return memoize_constraint_satisfaction (ci, args, result); |
| } |
| |
| } /* namespace */ |
| |
| /* Evaluate the given constraint, returning boolean_true_node |
| if the constraint is satisfied and boolean_false_node |
| otherwise. */ |
| |
| tree |
| evaluate_constraints (tree constr, tree args) |
| { |
| gcc_assert (constraint_p (constr)); |
| return satisfy_constraint (constr, args); |
| } |
| |
| /* Evaluate the function concept FN by substituting its own args |
| into its definition and evaluating that as the result. Returns |
| boolean_true_node if the constraints are satisfied and |
| boolean_false_node otherwise. */ |
| |
| tree |
| evaluate_function_concept (tree fn, tree args) |
| { |
| tree constr = build_nt (CHECK_CONSTR, fn, args); |
| return satisfy_constraint (constr, args); |
| } |
| |
| /* Evaluate the variable concept VAR by substituting its own args into |
| its initializer and checking the resulting constraint. Returns |
| boolean_true_node if the constraints are satisfied and |
| boolean_false_node otherwise. */ |
| |
| tree |
| evaluate_variable_concept (tree var, tree args) |
| { |
| tree constr = build_nt (CHECK_CONSTR, var, args); |
| return satisfy_constraint (constr, args); |
| } |
| |
| /* Evaluate the given expression as if it were a predicate |
| constraint. Returns boolean_true_node if the constraint |
| is satisfied and boolean_false_node otherwise. */ |
| |
| tree |
| evaluate_constraint_expression (tree expr, tree args) |
| { |
| tree constr = normalize_expression (expr); |
| return satisfy_constraint (constr, args); |
| } |
| |
| /* Returns true if the DECL's constraints are satisfied. |
| This is used in cases where a declaration is formed but |
| before it is used (e.g., overload resolution). */ |
| |
| bool |
| constraints_satisfied_p (tree decl) |
| { |
| /* Get the constraints to check for satisfaction. This depends |
| on whether we're looking at a template specialization or not. */ |
| tree ci; |
| tree args = NULL_TREE; |
| if (tree ti = DECL_TEMPLATE_INFO (decl)) |
| { |
| tree tmpl = TI_TEMPLATE (ti); |
| ci = get_constraints (tmpl); |
| int depth = TMPL_PARMS_DEPTH (DECL_TEMPLATE_PARMS (tmpl)); |
| args = get_innermost_template_args (TI_ARGS (ti), depth); |
| } |
| else |
| { |
| ci = get_constraints (decl); |
| } |
| |
| if (!push_tinst_level (decl)) |
| return true; |
| tree eval = satisfy_associated_constraints (ci, args); |
| pop_tinst_level (); |
| |
| return eval == boolean_true_node; |
| } |
| |
| /* Returns true if the constraints are satisfied by ARGS. |
| Here, T can be either a constraint or a constrained |
| declaration. */ |
| |
| bool |
| constraints_satisfied_p (tree t, tree args) |
| { |
| tree eval; |
| if (constraint_p (t)) |
| eval = evaluate_constraints (t, args); |
| else |
| eval = satisfy_associated_constraints (get_constraints (t), args); |
| return eval == boolean_true_node; |
| } |
| |
| namespace |
| { |
| |
| /* Normalize EXPR and determine if the resulting constraint is |
| satisfied by ARGS. Returns true if and only if the constraint |
| is satisfied. This is used extensively by diagnostics to |
| determine causes for failure. */ |
| |
| inline bool |
| constraint_expression_satisfied_p (tree expr, tree args) |
| { |
| return evaluate_constraint_expression (expr, args) == boolean_true_node; |
| } |
| |
| } /* namespace */ |
| |
| /*--------------------------------------------------------------------------- |
| Semantic analysis of requires-expressions |
| ---------------------------------------------------------------------------*/ |
| |
| /* Finish a requires expression for the given PARMS (possibly |
| null) and the non-empty sequence of requirements. */ |
| tree |
| finish_requires_expr (tree parms, tree reqs) |
| { |
| /* Modify the declared parameters by removing their context |
| so they don't refer to the enclosing scope and explicitly |
| indicating that they are constraint variables. */ |
| for (tree parm = parms; parm; parm = DECL_CHAIN (parm)) |
| { |
| DECL_CONTEXT (parm) = NULL_TREE; |
| CONSTRAINT_VAR_P (parm) = true; |
| } |
| |
| /* Build the node. */ |
| tree r = build_min (REQUIRES_EXPR, boolean_type_node, parms, reqs); |
| TREE_SIDE_EFFECTS (r) = false; |
| TREE_CONSTANT (r) = true; |
| return r; |
| } |
| |
| /* Construct a requirement for the validity of EXPR. */ |
| tree |
| finish_simple_requirement (tree expr) |
| { |
| return build_nt (SIMPLE_REQ, expr); |
| } |
| |
| /* Construct a requirement for the validity of TYPE. */ |
| tree |
| finish_type_requirement (tree type) |
| { |
| return build_nt (TYPE_REQ, type); |
| } |
| |
| /* Construct a requirement for the validity of EXPR, along with |
| its properties. if TYPE is non-null, then it specifies either |
| an implicit conversion or argument deduction constraint, |
| depending on whether any placeholders occur in the type name. |
| NOEXCEPT_P is true iff the noexcept keyword was specified. */ |
| tree |
| finish_compound_requirement (tree expr, tree type, bool noexcept_p) |
| { |
| tree req = build_nt (COMPOUND_REQ, expr, type); |
| COMPOUND_REQ_NOEXCEPT_P (req) = noexcept_p; |
| return req; |
| } |
| |
| /* Finish a nested requirement. */ |
| tree |
| finish_nested_requirement (tree expr) |
| { |
| return build_nt (NESTED_REQ, expr); |
| } |
| |
| // Check that FN satisfies the structural requirements of a |
| // function concept definition. |
| tree |
| check_function_concept (tree fn) |
| { |
| // Check that the function is comprised of only a single |
| // return statement. |
| tree body = DECL_SAVED_TREE (fn); |
| if (TREE_CODE (body) == BIND_EXPR) |
| body = BIND_EXPR_BODY (body); |
| |
| // Sometimes a function call results in the creation of clean up |
| // points. Allow these to be preserved in the body of the |
| // constraint, as we might actually need them for some constexpr |
| // evaluations. |
| if (TREE_CODE (body) == CLEANUP_POINT_EXPR) |
| body = TREE_OPERAND (body, 0); |
| |
| /* Check that the definition is written correctly. */ |
| if (TREE_CODE (body) != RETURN_EXPR) |
| { |
| location_t loc = DECL_SOURCE_LOCATION (fn); |
| if (TREE_CODE (body) == STATEMENT_LIST && !STATEMENT_LIST_HEAD (body)) |
| { |
| if (seen_error ()) |
| /* The definition was probably erroneous, not empty. */; |
| else |
| error_at (loc, "definition of concept %qD is empty", fn); |
| } |
| else |
| error_at (loc, "definition of concept %qD has multiple statements", fn); |
| } |
| |
| return NULL_TREE; |
| } |
| |
| |
| // Check that a constrained friend declaration function declaration, |
| // FN, is admissible. This is the case only when the declaration depends |
| // on template parameters and does not declare a specialization. |
| void |
| check_constrained_friend (tree fn, tree reqs) |
| { |
| if (fn == error_mark_node) |
| return; |
| gcc_assert (TREE_CODE (fn) == FUNCTION_DECL); |
| |
| // If there are not constraints, this cannot be an error. |
| if (!reqs) |
| return; |
| |
| // Constrained friend functions that don't depend on template |
| // arguments are effectively meaningless. |
| if (!uses_template_parms (TREE_TYPE (fn))) |
| { |
| error_at (location_of (fn), |
| "constrained friend does not depend on template parameters"); |
| return; |
| } |
| } |
| |
| /*--------------------------------------------------------------------------- |
| Equivalence of constraints |
| ---------------------------------------------------------------------------*/ |
| |
| /* Returns true when A and B are equivalent constraints. */ |
| bool |
| equivalent_constraints (tree a, tree b) |
| { |
| gcc_assert (!a || TREE_CODE (a) == CONSTRAINT_INFO); |
| gcc_assert (!b || TREE_CODE (b) == CONSTRAINT_INFO); |
| return cp_tree_equal (a, b); |
| } |
| |
| /* Returns true if the template declarations A and B have equivalent |
| constraints. This is the case when A's constraints subsume B's and |
| when B's also constrain A's. */ |
| bool |
| equivalently_constrained (tree d1, tree d2) |
| { |
| gcc_assert (TREE_CODE (d1) == TREE_CODE (d2)); |
| return equivalent_constraints (get_constraints (d1), get_constraints (d2)); |
| } |
| |
| /*--------------------------------------------------------------------------- |
| Partial ordering of constraints |
| ---------------------------------------------------------------------------*/ |
| |
| /* Returns true when the the constraints in A subsume those in B. */ |
| |
| bool |
| subsumes_constraints (tree a, tree b) |
| { |
| gcc_assert (!a || TREE_CODE (a) == CONSTRAINT_INFO); |
| gcc_assert (!b || TREE_CODE (b) == CONSTRAINT_INFO); |
| return subsumes (a, b); |
| } |
| |
| /* Returns true when the the constraints in A subsume those in B, but |
| the constraints in B do not subsume the constraints in A. */ |
| |
| bool |
| strictly_subsumes (tree a, tree b) |
| { |
| return subsumes (a, b) && !subsumes (b, a); |
| } |
| |
| /* Determines which of the declarations, A or B, is more constrained. |
| That is, which declaration's constraints subsume but are not subsumed |
| by the other's? |
| |
| Returns 1 if A is more constrained than B, -1 if B is more constrained |
| than A, and 0 otherwise. */ |
| |
| int |
| more_constrained (tree d1, tree d2) |
| { |
| tree c1 = get_constraints (d1); |
| tree c2 = get_constraints (d2); |
| int winner = 0; |
| if (subsumes_constraints (c1, c2)) |
| ++winner; |
| if (subsumes_constraints (c2, c1)) |
| --winner; |
| return winner; |
| } |
| |
| /* Returns true if D1 is at least as constrained as D2. That is, the |
| associated constraints of D1 subsume those of D2, or both declarations |
| are unconstrained. */ |
| |
| bool |
| at_least_as_constrained (tree d1, tree d2) |
| { |
| tree c1 = get_constraints (d1); |
| tree c2 = get_constraints (d2); |
| return subsumes_constraints (c1, c2); |
| } |
| |
| |
| /*--------------------------------------------------------------------------- |
| Constraint diagnostics |
| |
| FIXME: Normalize expressions into constraints before evaluating them. |
| This should be the general pattern for all such diagnostics. |
| ---------------------------------------------------------------------------*/ |
| |
| /* The number of detailed constraint failures. */ |
| |
| int constraint_errors = 0; |
| |
| /* Do not generate errors after diagnosing this number of constraint |
| failures. |
| |
| FIXME: This is a really arbitrary number. Provide better control of |
| constraint diagnostics with a command line option. */ |
| |
| int constraint_thresh = 20; |
| |
| |
| /* Returns true if we should elide the diagnostic for a constraint failure. |
| This is the case when the number of errors has exceeded the pre-configured |
| threshold. */ |
| |
| inline bool |
| elide_constraint_failure_p () |
| { |
| bool ret = constraint_thresh <= constraint_errors; |
| ++constraint_errors; |
| return ret; |
| } |
| |
| /* Returns the number of undiagnosed errors. */ |
| |
| inline int |
| undiagnosed_constraint_failures () |
| { |
| return constraint_errors - constraint_thresh; |
| } |
| |
| /* The diagnosis of constraints performs a combination of normalization |
| and satisfaction testing. We recursively walk through the conjunction or |
| disjunction of associated constraints, testing each sub-constraint in |
| turn. */ |
| |
| namespace { |
| |
| void diagnose_constraint (location_t, tree, tree, tree); |
| |
| /* Emit a specific diagnostics for a failed trait. */ |
| |
| void |
| diagnose_trait_expression (location_t loc, tree, tree cur, tree args) |
| { |
| if (constraint_expression_satisfied_p (cur, args)) |
| return; |
| if (elide_constraint_failure_p()) |
| return; |
| |
| tree expr = PRED_CONSTR_EXPR (cur); |
| ++processing_template_decl; |
| expr = tsubst_expr (expr, args, tf_none, NULL_TREE, false); |
| --processing_template_decl; |
| |
| tree t1 = TRAIT_EXPR_TYPE1 (expr); |
| tree t2 = TRAIT_EXPR_TYPE2 (expr); |
| switch (TRAIT_EXPR_KIND (expr)) |
| { |
| case CPTK_HAS_NOTHROW_ASSIGN: |
| inform (loc, " %qT is not nothrow copy assignable", t1); |
| break; |
| case CPTK_HAS_NOTHROW_CONSTRUCTOR: |
| inform (loc, " %qT is not nothrow default constructible", t1); |
| break; |
| case CPTK_HAS_NOTHROW_COPY: |
| inform (loc, " %qT is not nothrow copy constructible", t1); |
| break; |
| case CPTK_HAS_TRIVIAL_ASSIGN: |
| inform (loc, " %qT is not trivially copy assignable", t1); |
| break; |
| case CPTK_HAS_TRIVIAL_CONSTRUCTOR: |
| inform (loc, " %qT is not trivially default constructible", t1); |
| break; |
| case CPTK_HAS_TRIVIAL_COPY: |
| inform (loc, " %qT is not trivially copy constructible", t1); |
| break; |
| case CPTK_HAS_TRIVIAL_DESTRUCTOR: |
| inform (loc, " %qT is not trivially destructible", t1); |
| break; |
| case CPTK_HAS_VIRTUAL_DESTRUCTOR: |
| inform (loc, " %qT does not have a virtual destructor", t1); |
| break; |
| case CPTK_IS_ABSTRACT: |
| inform (loc, " %qT is not an abstract class", t1); |
| break; |
| case CPTK_IS_BASE_OF: |
| inform (loc, " %qT is not a base of %qT", t1, t2); |
| break; |
| case CPTK_IS_CLASS: |
| inform (loc, " %qT is not a class", t1); |
| break; |
| case CPTK_IS_EMPTY: |
| inform (loc, " %qT is not an empty class", t1); |
| break; |
| case CPTK_IS_ENUM: |
| inform (loc, " %qT is not an enum", t1); |
| break; |
| case CPTK_IS_FINAL: |
| inform (loc, " %qT is not a final class", t1); |
| break; |
| case CPTK_IS_LITERAL_TYPE: |
| inform (loc, " %qT is not a literal type", t1); |
| break; |
| case CPTK_IS_POD: |
| inform (loc, " %qT is not a POD type", t1); |
| break; |
| case CPTK_IS_POLYMORPHIC: |
| inform (loc, " %qT is not a polymorphic type", t1); |
| break; |
| case CPTK_IS_SAME_AS: |
| inform (loc, " %qT is not the same as %qT", t1, t2); |
| break; |
| case CPTK_IS_STD_LAYOUT: |
| inform (loc, " %qT is not an standard layout type", t1); |
| break; |
| case CPTK_IS_TRIVIAL: |
| inform (loc, " %qT is not a trivial type", t1); |
| break; |
| case CPTK_IS_UNION: |
| inform (loc, " %qT is not a union", t1); |
| break; |
| default: |
| gcc_unreachable (); |
| } |
| } |
| |
| /* Diagnose the expression of a predicate constraint. */ |
| |
| void |
| diagnose_other_expression (location_t loc, tree, tree cur, tree args) |
| { |
| if (constraint_expression_satisfied_p (cur, args)) |
| return; |
| if (elide_constraint_failure_p()) |
| return; |
| inform (loc, "%qE evaluated to false", cur); |
| } |
| |
| /* Do our best to infer meaning from predicates. */ |
| |
| inline void |
| diagnose_predicate_constraint (location_t loc, tree orig, tree cur, tree args) |
| { |
| if (TREE_CODE (PRED_CONSTR_EXPR (cur)) == TRAIT_EXPR) |
| diagnose_trait_expression (loc, orig, cur, args); |
| else |
| diagnose_other_expression (loc, orig, cur, args); |
| } |
| |
| /* Diagnose a failed pack expansion, possibly containing constraints. */ |
| |
| void |
| diagnose_pack_expansion (location_t loc, tree, tree cur, tree args) |
| { |
| if (constraint_expression_satisfied_p (cur, args)) |
| return; |
| if (elide_constraint_failure_p()) |
| return; |
| |
| /* Make sure that we don't have naked packs that we don't expect. */ |
| if (!same_type_p (TREE_TYPE (cur), boolean_type_node)) |
| { |
| inform (loc, "invalid pack expansion in constraint %qE", cur); |
| return; |
| } |
| |
| inform (loc, "in the expansion of %qE", cur); |
| |
| /* Get the vector of expanded arguments. Note that n must not |
| be 0 since this constraint is not satisfied. */ |
| ++processing_template_decl; |
| tree exprs = tsubst_pack_expansion (cur, args, tf_none, NULL_TREE); |
| --processing_template_decl; |
| if (exprs == error_mark_node) |
| { |
| /* TODO: This error message could be better. */ |
| inform (loc, " substitution failure occurred during expansion"); |
| return; |
| } |
| |
| /* Check each expanded constraint separately. */ |
| int n = TREE_VEC_LENGTH (exprs); |
| for (int i = 0; i < n; ++i) |
| { |
| tree expr = TREE_VEC_ELT (exprs, i); |
| if (!constraint_expression_satisfied_p (expr, args)) |
| inform (loc, " %qE was not satisfied", expr); |
| } |
| } |
| |
| /* Diagnose a potentially unsatisfied concept check constraint DECL<CARGS>. |
| Parameters are as for diagnose_constraint. */ |
| |
| void |
| diagnose_check_constraint (location_t loc, tree orig, tree cur, tree args) |
| { |
| if (constraints_satisfied_p (cur, args)) |
| return; |
| |
| tree decl = CHECK_CONSTR_CONCEPT (cur); |
| tree cargs = CHECK_CONSTR_ARGS (cur); |
| tree tmpl = DECL_TI_TEMPLATE (decl); |
| tree check = build_nt (CHECK_CONSTR, decl, cargs); |
| |
| /* Instantiate the concept check arguments. */ |
| tree targs = tsubst (cargs, args, tf_none, NULL_TREE); |
| if (targs == error_mark_node) |
| { |
| if (elide_constraint_failure_p ()) |
| return; |
| inform (loc, "invalid use of the concept %qE", check); |
| tsubst (cargs, args, tf_warning_or_error, NULL_TREE); |
| return; |
| } |
| |
| tree sub = build_tree_list (tmpl, targs); |
| /* Update to the expanded definitions. */ |
| cur = expand_concept (decl, targs); |
| if (cur == error_mark_node) |
| { |
| if (elide_constraint_failure_p ()) |
| return; |
| inform (loc, "in the expansion of concept %<%E %S%>", check, sub); |
| cur = get_concept_definition (decl); |
| tsubst_expr (cur, targs, tf_warning_or_error, NULL_TREE, false); |
| return; |
| } |
| |
| orig = get_concept_definition (CHECK_CONSTR_CONCEPT (orig)); |
| orig = normalize_expression (orig); |
| |
| location_t dloc = DECL_SOURCE_LOCATION (decl); |
| inform (dloc, "within %qS", sub); |
| diagnose_constraint (dloc, orig, cur, targs); |
| } |
| |
| /* Diagnose a potentially unsatisfied conjunction or disjunction. Parameters |
| are as for diagnose_constraint. */ |
| |
| void |
| diagnose_logical_constraint (location_t loc, tree orig, tree cur, tree args) |
| { |
| tree t0 = TREE_OPERAND (cur, 0); |
| tree t1 = TREE_OPERAND (cur, 1); |
| if (!constraints_satisfied_p (t0, args)) |
| diagnose_constraint (loc, TREE_OPERAND (orig, 0), t0, args); |
| else if (TREE_CODE (orig) == TRUTH_ORIF_EXPR) |
| return; |
| if (!constraints_satisfied_p (t1, args)) |
| diagnose_constraint (loc, TREE_OPERAND (orig, 1), t1, args); |
| } |
| |
| /* Diagnose a potential expression constraint failure. */ |
| |
| void |
| diagnose_expression_constraint (location_t loc, tree orig, tree cur, tree args) |
| { |
| if (constraints_satisfied_p (cur, args)) |
| return; |
| if (elide_constraint_failure_p()) |
| return; |
| |
| tree expr = EXPR_CONSTR_EXPR (orig); |
| inform (loc, "the required expression %qE would be ill-formed", expr); |
| |
| // TODO: We should have a flag that controls this substitution. |
| // I'm finding it very useful for resolving concept check errors. |
| |
| // inform (input_location, "==== BEGIN DUMP ===="); |
| // tsubst_expr (EXPR_CONSTR_EXPR (orig), args, tf_warning_or_error, NULL_TREE, false); |
| // inform (input_location, "==== END DUMP ===="); |
| } |
| |
| /* Diagnose a potentially failed type constraint. */ |
| |
| void |
| diagnose_type_constraint (location_t loc, tree orig, tree cur, tree args) |
| { |
| if (constraints_satisfied_p (cur, args)) |
| return; |
| if (elide_constraint_failure_p()) |
| return; |
| |
| tree type = TYPE_CONSTR_TYPE (orig); |
| inform (loc, "the required type %qT would be ill-formed", type); |
| } |
| |
| /* Diagnose a potentially unsatisfied conversion constraint. */ |
| |
| void |
| diagnose_implicit_conversion_constraint (location_t loc, tree orig, tree cur, |
| tree args) |
| { |
| if (constraints_satisfied_p (cur, args)) |
| return; |
| |
| /* The expression and type will previously have been substituted into, |
| and therefore may already be an error. Also, we will have already |
| diagnosed substitution failures into an expression since this must be |
| part of a compound requirement. */ |
| tree expr = ICONV_CONSTR_EXPR (cur); |
| if (error_operand_p (expr)) |
| return; |
| |
| /* Don't elide a previously diagnosed failure. */ |
| if (elide_constraint_failure_p()) |
| return; |
| |
| tree type = ICONV_CONSTR_TYPE (cur); |
| if (error_operand_p (type)) |
| { |
| inform (loc, "substitution into type %qT failed", |
| ICONV_CONSTR_TYPE (orig)); |
| return; |
| } |
| |
| inform(loc, "%qE is not implicitly convertible to %qT", expr, type); |
| } |
| |
| /* Diagnose an argument deduction constraint. */ |
| |
| void |
| diagnose_argument_deduction_constraint (location_t loc, tree orig, tree cur, |
| tree args) |
| { |
| if (constraints_satisfied_p (cur, args)) |
| return; |
| |
| /* The expression and type will previously have been substituted into, |
| and therefore may already be an error. Also, we will have already |
| diagnosed substution failures into an expression since this must be |
| part of a compound requirement. */ |
| tree expr = DEDUCT_CONSTR_EXPR (cur); |
| if (error_operand_p (expr)) |
| return; |
| |
| /* Don't elide a previously diagnosed failure. */ |
| if (elide_constraint_failure_p ()) |
| return; |
| |
| tree pattern = DEDUCT_CONSTR_PATTERN (cur); |
| if (error_operand_p (pattern)) |
| { |
| inform (loc, "substitution into type %qT failed", |
| DEDUCT_CONSTR_PATTERN (orig)); |
| return; |
| } |
| |
| inform (loc, "unable to deduce placeholder type %qT from %qE", |
| pattern, expr); |
| } |
| |
| /* Diagnose an exception constraint. */ |
| |
| void |
| diagnose_exception_constraint (location_t loc, tree orig, tree cur, tree args) |
| { |
| if (constraints_satisfied_p (cur, args)) |
| return; |
| if (elide_constraint_failure_p ()) |
| return; |
| |
| /* Rebuild a noexcept expression. */ |
| tree expr = EXCEPT_CONSTR_EXPR (cur); |
| if (error_operand_p (expr)) |
| return; |
| |
| inform (loc, "%qE evaluated to false", EXCEPT_CONSTR_EXPR (orig)); |
| } |
| |
| /* Diagnose a potentially unsatisfied parameterized constraint. */ |
| |
| void |
| diagnose_parameterized_constraint (location_t loc, tree orig, tree cur, |
| tree args) |
| { |
| if (constraints_satisfied_p (cur, args)) |
| return; |
| |
| local_specialization_stack stack; |
| tree parms = PARM_CONSTR_PARMS (cur); |
| tree vars = tsubst_constraint_variables (parms, args, tf_warning_or_error, |
| NULL_TREE); |
| if (vars == error_mark_node) |
| { |
| if (elide_constraint_failure_p ()) |
| return; |
| |
| /* TODO: Check which variable failed and use orig to diagnose |
| that substitution error. */ |
| inform (loc, "failed to instantiate constraint variables"); |
| return; |
| } |
| |
| /* TODO: It would be better write these in a list. */ |
| while (vars) |
| { |
| inform (loc, " with %q#D", vars); |
| vars = TREE_CHAIN (vars); |
| } |
| orig = PARM_CONSTR_OPERAND (orig); |
| cur = PARM_CONSTR_OPERAND (cur); |
| return diagnose_constraint (loc, orig, cur, args); |
| } |
| |
| /* Diagnose the constraint CUR for the given ARGS. This is only ever invoked |
| on the associated constraints, so we can only have conjunctions of |
| predicate constraints. The ORIGinal (dependent) constructs follow |
| the current constraints to enable better diagnostics. Note that ORIG |
| and CUR must be the same kinds of node, except when CUR is an error. */ |
| |
| void |
| diagnose_constraint (location_t loc, tree orig, tree cur, tree args) |
| { |
| switch (TREE_CODE (cur)) |
| { |
| case EXPR_CONSTR: |
| diagnose_expression_constraint (loc, orig, cur, args); |
| break; |
| |
| case TYPE_CONSTR: |
| diagnose_type_constraint (loc, orig, cur, args); |
| break; |
| |
| case ICONV_CONSTR: |
| diagnose_implicit_conversion_constraint (loc, orig, cur, args); |
| break; |
| |
| case DEDUCT_CONSTR: |
| diagnose_argument_deduction_constraint (loc, orig, cur, args); |
| break; |
| |
| case EXCEPT_CONSTR: |
| diagnose_exception_constraint (loc, orig, cur, args); |
| break; |
| |
| case CONJ_CONSTR: |
| case DISJ_CONSTR: |
| diagnose_logical_constraint (loc, orig, cur, args); |
| break; |
| |
| case PRED_CONSTR: |
| diagnose_predicate_constraint (loc, orig, cur, args); |
| break; |
| |
| case PARM_CONSTR: |
| diagnose_parameterized_constraint (loc, orig, cur, args); |
| break; |
| |
| case CHECK_CONSTR: |
| diagnose_check_constraint (loc, orig, cur, args); |
| break; |
| |
| case EXPR_PACK_EXPANSION: |
| diagnose_pack_expansion (loc, orig, cur, args); |
| break; |
| |
| case ERROR_MARK: |
| /* TODO: Can we improve the diagnostic with the original? */ |
| inform (input_location, "ill-formed constraint"); |
| break; |
| |
| default: |
| gcc_unreachable (); |
| break; |
| } |
| } |
| |
| /* Diagnose the reason(s) why ARGS do not satisfy the constraints |
| of declaration DECL. */ |
| |
| void |
| diagnose_declaration_constraints (location_t loc, tree decl, tree args) |
| { |
| inform (loc, " constraints not satisfied"); |
| |
| /* Constraints are attached to the template. */ |
| if (tree ti = DECL_TEMPLATE_INFO (decl)) |
| { |
| decl = TI_TEMPLATE (ti); |
| if (!args) |
| args = TI_ARGS (ti); |
| } |
| |
| /* Recursively diagnose the associated constraints. */ |
| tree ci = get_constraints (decl); |
| tree t = CI_ASSOCIATED_CONSTRAINTS (ci); |
| diagnose_constraint (loc, t, t, args); |
| } |
| |
| } // namespace |
| |
| /* Emit diagnostics detailing the failure ARGS to satisfy the |
| constraints of T. Here, T can be either a constraint |
| or a declaration. */ |
| |
| void |
| diagnose_constraints (location_t loc, tree t, tree args) |
| { |
| constraint_errors = 0; |
| |
| if (constraint_p (t)) |
| diagnose_constraint (loc, t, t, args); |
| else if (DECL_P (t)) |
| diagnose_declaration_constraints (loc, t, args); |
| else |
| gcc_unreachable (); |
| |
| /* Note the number of elided failures. */ |
| int n = undiagnosed_constraint_failures (); |
| if (n > 0) |
| inform (loc, "... and %d more constraint errors not shown", n); |
| } |