Mercurial > hg > CbC > CbC_gcc
diff gcc/cp/constraint.cc @ 111:04ced10e8804
gcc 7
author | kono |
---|---|
date | Fri, 27 Oct 2017 22:46:09 +0900 |
parents | |
children | 84e7813d76e9 |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/gcc/cp/constraint.cc Fri Oct 27 22:46:09 2017 +0900 @@ -0,0 +1,3146 @@ +/* Processing rules for constraints. + Copyright (C) 2013-2017 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 = 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; + + 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) + 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 (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); + } + + tree eval = satisfy_associated_constraints (ci, args); + 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); +}