Mercurial > hg > CbC > CbC_gcc
diff gcc/cp/constraint.cc @ 145:1830386684a0
gcc-9.2.0
author | anatofuz |
---|---|
date | Thu, 13 Feb 2020 11:34:05 +0900 |
parents | 84e7813d76e9 |
children |
line wrap: on
line diff
--- a/gcc/cp/constraint.cc Thu Oct 25 07:37:49 2018 +0900 +++ b/gcc/cp/constraint.cc Thu Feb 13 11:34:05 2020 +0900 @@ -1,5 +1,5 @@ /* Processing rules for constraints. - Copyright (C) 2013-2018 Free Software Foundation, Inc. + Copyright (C) 2013-2020 Free Software Foundation, Inc. Contributed by Andrew Sutton (andrew.n.sutton@gmail.com) This file is part of GCC. @@ -46,89 +46,191 @@ #include "toplev.h" #include "type-utils.h" +static tree satisfaction_value (tree t); + +/* When we're parsing or substuting a constraint expression, we have slightly + different expression semantics. In particular, we don't want to reduce a + concept-id to a satisfaction value. */ + +processing_constraint_expression_sentinel:: +processing_constraint_expression_sentinel () +{ + ++scope_chain->x_processing_constraint; +} + +processing_constraint_expression_sentinel:: +~processing_constraint_expression_sentinel () +{ + --scope_chain->x_processing_constraint; +} + +bool +processing_constraint_expression_p () +{ + return scope_chain->x_processing_constraint != 0; +} + /*--------------------------------------------------------------------------- - Operations on constraints + Constraint expressions ---------------------------------------------------------------------------*/ -/* Returns true if C is a constraint tree code. Note that ERROR_MARK - is a valid constraint. */ +/* Information provided to substitution. */ + +struct subst_info +{ + subst_info (tsubst_flags_t cmp, tree in) + : complain (cmp), in_decl (in) + { } + + /* True if we should not diagnose errors. */ + bool quiet() const + { + return complain == tf_none; + } + + /* True if we should diagnose errors. */ + bool noisy() const + { + return !quiet (); + } + + tsubst_flags_t complain; + tree in_decl; +}; + +static tree satisfy_constraint (tree, tree, subst_info); + +/* True if T is known to be some type other than bool. Note that this + is false for dependent types and errors. */ static inline bool -constraint_p (tree_code c) +known_non_bool_p (tree t) { - return ((PRED_CONSTR <= c && c <= DISJ_CONSTR) - || c == EXPR_PACK_EXPANSION - || c == ERROR_MARK); + return (t && !WILDCARD_TYPE_P (t) && TREE_CODE (t) != BOOLEAN_TYPE); } -/* Returns true if T is a constraint. Note that error_mark_node - is a valid constraint. */ - -bool -constraint_p (tree t) +static bool +check_constraint_atom (cp_expr expr) +{ + if (known_non_bool_p (TREE_TYPE (expr))) + { + error_at (expr.get_location (), + "constraint expression does not have type %<bool%>"); + return false; + } + + /* Check that we're using function concepts correctly. */ + if (concept_check_p (expr)) + { + tree id = unpack_concept_check (expr); + tree tmpl = TREE_OPERAND (id, 0); + if (OVL_P (tmpl) && TREE_CODE (expr) == TEMPLATE_ID_EXPR) + { + error_at (EXPR_LOC_OR_LOC (expr, input_location), + "function concept must be called"); + return false; + } + } + + return true; +} + +static bool +check_constraint_operands (location_t, cp_expr lhs, cp_expr rhs) { - return constraint_p (TREE_CODE (t)); + return check_constraint_atom (lhs) && check_constraint_atom (rhs); } -/* 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. */ +/* Validate the semantic properties of the constraint expression. */ + +static cp_expr +finish_constraint_binary_op (location_t loc, + tree_code code, + cp_expr lhs, + cp_expr rhs) +{ + gcc_assert (processing_constraint_expression_p ()); + if (lhs == error_mark_node || rhs == error_mark_node) + return error_mark_node; + if (!check_constraint_operands (loc, lhs, rhs)) + return error_mark_node; + tree overload; + tree expr = build_x_binary_op (loc, code, + lhs, TREE_CODE (lhs), + rhs, TREE_CODE (rhs), + &overload, tf_none); + /* When either operand is dependent, the overload set may be non-empty. */ + if (expr == error_mark_node) + return error_mark_node; + SET_EXPR_LOCATION (expr, loc); + return expr; +} + +cp_expr +finish_constraint_or_expr (location_t loc, cp_expr lhs, cp_expr rhs) +{ + return finish_constraint_binary_op (loc, TRUTH_ORIF_EXPR, lhs, rhs); +} + +cp_expr +finish_constraint_and_expr (location_t loc, cp_expr lhs, cp_expr rhs) +{ + return finish_constraint_binary_op (loc, TRUTH_ANDIF_EXPR, lhs, rhs); +} + +cp_expr +finish_constraint_primary_expr (cp_expr expr) +{ + if (expr == error_mark_node) + return error_mark_node; + if (!check_constraint_atom (expr)) + return cp_expr (error_mark_node, expr.get_location ()); + return expr; +} + +/* Combine two constraint-expressions with a logical-and. */ tree -conjoin_constraints (tree a, tree b) +combine_constraint_expressions (tree lhs, tree rhs) { - 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; + processing_constraint_expression_sentinel pce; + if (!lhs) + return rhs; + if (!rhs) + return lhs; + return finish_constraint_and_expr (input_location, lhs, rhs); } -/* Transform the vector of expressions in the T into a conjunction - of requirements. T must be a TREE_VEC. */ +/* Extract the template-id from a concept check. For standard and variable + checks, this is simply T. For function concept checks, this is the + called function. */ tree -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) +unpack_concept_check (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; + gcc_assert (concept_check_p (t)); + + if (TREE_CODE (t) == CALL_EXPR) + t = CALL_EXPR_FN (t); + + gcc_assert (TREE_CODE (t) == TEMPLATE_ID_EXPR); + return t; } -/* Returns true if any of the arguments in the template - argument list is a wildcard or wildcard pack. */ +/* Extract the TEMPLATE_DECL from a concept check. */ + +tree +get_concept_check_template (tree t) +{ + tree id = unpack_concept_check (t); + tree tmpl = TREE_OPERAND (id, 0); + if (OVL_P (tmpl)) + tmpl = OVL_FIRST (tmpl); + return tmpl; +} + +/* Returns true if any of the arguments in the template argument list is + a wildcard or wildcard pack. */ bool contains_wildcard_p (tree args) @@ -142,89 +244,56 @@ 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. +/* 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) +resolve_function_concept_overload (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. + /* 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. + /* 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)) @@ -247,29 +316,30 @@ 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. +/* 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) +resolve_function_concept_check (tree call) { gcc_assert (TREE_CODE (call) == CALL_EXPR); - // A constraint check must be only a template-id expression. If - // it's a call to a base-link, its function(s) should be a - // template-id expression. If this is not a template-id, then it - // cannot be a concept-check. + /* 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. + /* 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. */ + /* This is a function call of a variable concept... ill-formed. */ if (TREE_CODE (ovl) == TEMPLATE_DECL) { error_at (location_of (call), @@ -278,59 +348,52 @@ } tree args = TREE_OPERAND (target, 1); - return resolve_constraint_check (ovl, args); + return resolve_function_concept_overload (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. */ +/* Returns a pair containing the checked concept and its associated + prototype parameter. The result is a TREE_LIST whose TREE_VALUE + is the concept (non-template) and whose TREE_PURPOSE contains + the converted template arguments, including the deduced prototype + parameter (in position 0). */ tree -resolve_variable_concept_check (tree id) +resolve_concept_check (tree check) { + gcc_assert (concept_check_p (check)); + tree id = unpack_concept_check (check); tree tmpl = TREE_OPERAND (id, 0); + + /* If this is an overloaded function concept, perform overload + resolution (this only happens when deducing prototype parameters + and template introductions). */ + if (TREE_CODE (tmpl) == OVERLOAD) + { + if (OVL_CHAIN (tmpl)) + return resolve_function_concept_check (check); + tmpl = OVL_FIRST (tmpl); + } + tree args = TREE_OPERAND (id, 1); - - 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 + if (result == error_mark_node) return error_mark_node; + return build_tree_list (result, DECL_TEMPLATE_RESULT (tmpl)); } - -/* Given a call expression or template-id expression to - a concept EXPR possibly including a wildcard, deduce - the concept being checked and the prototype parameter. - Returns true if the constraint and prototype can be - deduced and false otherwise. Note that the CHECK and - PROTO arguments are set to NULL_TREE if this returns - false. */ +/* 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 (); - + tree info = resolve_concept_check (expr); if (info && info != error_mark_node) { check = TREE_VALUE (info); @@ -340,135 +403,48 @@ 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. +/* 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) +deduce_concept_introduction (tree check) { - 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 (); - + tree info = resolve_concept_check (check); 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) +/* Build a constrained placeholder type where SPEC is a type-constraint. + SPEC can be anything were concept_definition_p is true. + + If DECLTYPE_P is true, then the placeholder is decltype(auto). + + Returns a pair whose FIRST is the concept being checked and whose + SECOND is the prototype parameter. */ + +tree_pair +finish_type_constraints (tree spec, tree args, tsubst_flags_t complain) { - 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); + gcc_assert (concept_definition_p (spec)); + + /* Build an initial concept check. */ + tree check = build_type_constraint (spec, args, complain); + if (check == error_mark_node) + return std::make_pair (error_mark_node, NULL_TREE); + + /* Extract the concept and prototype parameter from the check. */ + tree con; + tree proto; + if (!deduce_constrained_parameter (check, con, proto)) + return std::make_pair (error_mark_node, NULL_TREE); + + return std::make_pair (con, proto); } /*--------------------------------------------------------------------------- @@ -477,7 +453,7 @@ /* Returns the expression of a function concept. */ -tree +static tree get_returned_expression (tree fn) { /* Extract the body of the function minus the return expression. */ @@ -494,322 +470,238 @@ /* Returns the initializer of a variable concept. */ -tree +static tree get_variable_initializer (tree var) { tree init = DECL_INITIAL (var); if (!init) return error_mark_node; + if (BRACE_ENCLOSED_INITIALIZER_P (init) + && CONSTRUCTOR_NELTS (init) == 1) + init = CONSTRUCTOR_ELT (init, 0)->value; return init; } /* Returns the definition of a variable or function concept. */ -tree +static tree get_concept_definition (tree decl) { + if (TREE_CODE (decl) == OVERLOAD) + decl = OVL_FIRST (decl); + + if (TREE_CODE (decl) == TEMPLATE_DECL) + decl = DECL_TEMPLATE_RESULT (decl); + + if (TREE_CODE (decl) == CONCEPT_DECL) + return DECL_INITIAL (decl); if (VAR_P (decl)) return get_variable_initializer (decl); - else if (TREE_CODE (decl) == FUNCTION_DECL) + 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 + 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. +in a sequence of steps. ---------------------------------------------------------------------------*/ +void +debug_parameter_mapping (tree map) +{ + for (tree p = map; p; p = TREE_CHAIN (p)) + { + tree parm = TREE_VALUE (p); + tree arg = TREE_PURPOSE (p); + if (TYPE_P (parm)) + verbatim ("MAP %qD TO %qT", TEMPLATE_TYPE_DECL (parm), arg); + else + verbatim ("MAP %qD TO %qE", TEMPLATE_PARM_DECL (parm), arg); + // debug_tree (parm); + // debug_tree (arg); + } +} + +void +debug_argument_list (tree args) +{ + for (int i = 0; i < TREE_VEC_LENGTH (args); ++i) + { + tree arg = TREE_VEC_ELT (args, i); + if (TYPE_P (arg)) + verbatim ("ARG %qT", arg); + else + verbatim ("ARG %qE", arg); + } +} + +/* Associate each parameter in PARMS with its corresponding template + argument in ARGS. */ + +static tree +map_arguments (tree parms, tree args) +{ + for (tree p = parms; p; p = TREE_CHAIN (p)) + { + int level; + int index; + template_parm_level_and_index (TREE_VALUE (p), &level, &index); + TREE_PURPOSE (p) = TMPL_ARG (args, level, index); + } + return parms; +} + +/* Build the parameter mapping for EXPR using ARGS. */ + +static tree +build_parameter_mapping (tree expr, tree args, tree decl) +{ + tree ctx_parms = NULL_TREE; + if (decl) + { + gcc_assert (TREE_CODE (decl) == TEMPLATE_DECL); + ctx_parms = DECL_TEMPLATE_PARMS (decl); + } + else if (current_template_parms) + { + /* TODO: This should probably be the only case, but because the + point of declaration of concepts is currently set after the + initializer, the template parameter lists are not available + when normalizing concept definitions, hence the case above. */ + ctx_parms = current_template_parms; + } + + tree parms = find_template_parameters (expr, ctx_parms); + tree map = map_arguments (parms, args); + return map; +} + +/* True if the parameter mappings of two atomic constraints are equivalent. */ + +static bool +parameter_mapping_equivalent_p (tree t1, tree t2) +{ + tree map1 = ATOMIC_CONSTR_MAP (t1); + tree map2 = ATOMIC_CONSTR_MAP (t2); + while (map1 && map2) + { + tree arg1 = TREE_PURPOSE (map1); + tree arg2 = TREE_PURPOSE (map2); + if (!template_args_equal (arg1, arg2)) + return false; + map1 = TREE_CHAIN (map1); + map2 = TREE_CHAIN (map2); + } + return true; +} + +/* Provides additional context for normalization. */ + +struct norm_info : subst_info +{ + explicit norm_info (tsubst_flags_t complain) + : subst_info (tf_warning_or_error | complain, NULL_TREE), + context() + {} + + /* Construct a top-level context for DECL. */ + + norm_info (tree in_decl, tsubst_flags_t complain) + : subst_info (tf_warning_or_error | complain, in_decl), + context (make_context (in_decl)) + {} + + bool generate_diagnostics() const + { + return complain & tf_norm; + } + + tree make_context(tree in_decl) + { + if (generate_diagnostics ()) + return build_tree_list (NULL_TREE, in_decl); + return NULL_TREE; + } + + void update_context(tree expr, tree args) + { + if (generate_diagnostics ()) + { + tree map = build_parameter_mapping (expr, args, in_decl); + context = tree_cons (map, expr, context); + } + in_decl = get_concept_check_template (expr); + } + + /* Provides information about the source of a constraint. This is a + TREE_LIST whose VALUE is either a concept check or a constrained + declaration. The PURPOSE, for concept checks is a parameter mapping + for that check. */ + + tree context; +}; + +static tree normalize_expression (tree, tree, norm_info); + /* 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) +static tree +normalize_logical_operation (tree t, tree args, tree_code c, norm_info info) { - 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; + tree t0 = normalize_expression (TREE_OPERAND (t, 0), args, info); + tree t1 = normalize_expression (TREE_OPERAND (t, 1), args, info); + + /* Build a new info object for the constraint. */ + tree ci = info.generate_diagnostics() + ? build_tree_list (t, info.context) + : NULL_TREE; + + return build2 (c, ci, t0, t1); } -/* 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) +static tree +normalize_concept_check (tree check, tree args, norm_info info) { - 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 id = unpack_concept_check (check); + tree tmpl = TREE_OPERAND (id, 0); + tree targs = TREE_OPERAND (id, 1); + + /* A function concept is wrapped in an overload. */ + if (TREE_CODE (tmpl) == OVERLOAD) { - 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; + /* TODO: Can we diagnose this error during parsing? */ + if (TREE_CODE (check) == TEMPLATE_ID_EXPR) + error_at (EXPR_LOC_OR_LOC (check, input_location), + "function concept must be called"); + tmpl = OVL_FIRST (tmpl); } - return build_nt (PRED_CONSTR, t); -} - -/* For a call expression to a function concept, returns a check - constraint. Otherwise, returns a predicate constraint. */ - -tree -normalize_call_expression (tree t) -{ - /* Try to resolve this function call as a concept. If not, then - it can be returned as a predicate constraint. */ - tree check = resolve_constraint_check (t); - if (!check) - return build_nt (PRED_CONSTR, t); - if (check == error_mark_node) - { - /* TODO: Improve diagnostics. We could report why the reference - is invalid. */ - error ("invalid reference to concept %qE", t); - return error_mark_node; - } - - tree fn = TREE_VALUE (check); - tree args = TREE_PURPOSE (check); - return build_nt (CHECK_CONSTR, fn, args); -} - -/* If T is a call to an overloaded && or || operator, diagnose that - as a non-SFINAEable error. Returns true if an error is emitted. - - TODO: It would be better to diagnose this at the point of definition, - if possible. Perhaps we should immediately do a first-pass normalization - of a concept definition to catch obvious non-dependent errors like - this. */ - -bool -check_for_logical_overloads (tree t) -{ - if (TREE_CODE (t) != CALL_EXPR) - return false; - - tree fn = CALL_EXPR_FN (t); - - /* For member calls, try extracting the function from the - component ref. */ - if (TREE_CODE (fn) == COMPONENT_REF) - { - fn = TREE_OPERAND (fn, 1); - if (TREE_CODE (fn) == BASELINK) - fn = BASELINK_FUNCTIONS (fn); - } - - if (TREE_CODE (fn) != FUNCTION_DECL) - return false; - - if (DECL_OVERLOADED_OPERATOR_P (fn)) - { - location_t loc = cp_expr_loc_or_loc (t, input_location); - error_at (loc, "constraint %qE, uses overloaded operator", t); - return true; - } - - return false; + /* Substitute through the arguments of the concept check. */ + targs = tsubst_template_args (targs, args, info.complain, info.in_decl); + if (targs == error_mark_node) + return error_mark_node; + + /* Build the substitution for the concept definition. */ + tree parms = TREE_VALUE (DECL_TEMPLATE_PARMS (tmpl)); + /* Turn on template processing; coercing non-type template arguments + will automatically assume they're non-dependent. */ + ++processing_template_decl; + tree subst = coerce_template_parms (parms, targs, tmpl); + --processing_template_decl; + if (subst == error_mark_node) + return error_mark_node; + + /* The concept may have been ill-formed. */ + tree def = get_concept_definition (DECL_TEMPLATE_RESULT (tmpl)); + if (def == error_mark_node) + return error_mark_node; + + info.update_context (check, args); + return normalize_expression (def, subst, info); } /* The normal form of an atom depends on the expression. The normal @@ -818,135 +710,26 @@ 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) +static tree +normalize_atom (tree t, tree args, norm_info info) { - 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); - } + /* Concept checks are not atomic. */ + if (concept_check_p (t)) + return normalize_concept_check (t, args, info); + + /* Build the parameter mapping for the atom. */ + tree map = build_parameter_mapping (t, args, info.in_decl); + + /* Build a new info object for the atom. */ + tree ci = build_tree_list (t, info.context); + + return build1 (ATOMIC_CONSTR, ci, map); } -/* 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) +/* Returns the normal form of an expression. */ + +static tree +normalize_expression (tree t, tree args, norm_info info) { if (!t) return NULL_TREE; @@ -954,133 +737,297 @@ 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(); + case TRUTH_ANDIF_EXPR: + return normalize_logical_operation (t, args, CONJ_CONSTR, info); + case TRUTH_ORIF_EXPR: + return normalize_logical_operation (t, args, DISJ_CONSTR, info); + default: + return normalize_atom (t, args, info); + } +} + +/* Cache of the normalized form of constraints. Marked as deletable because it + can all be recalculated. */ +static GTY((deletable)) hash_map<tree,tree> *normalized_map; + +static tree +get_normalized_constraints (tree t, tree args, norm_info info) +{ + auto_timevar time (TV_CONSTRAINT_NORM); + return normalize_expression (t, args, info); +} + +/* Returns the normalized constraints from a constraint-info object + or NULL_TREE if the constraints are null. ARGS provide the initial + arguments for normalization and IN_DECL provides the declaration + to which the constraints belong. */ + +static tree +get_normalized_constraints_from_info (tree ci, tree args, tree in_decl, + bool diag = false) +{ + if (ci == NULL_TREE) + return NULL_TREE; + + /* Substitution errors during normalization are fatal. */ + ++processing_template_decl; + norm_info info (in_decl, diag ? tf_norm : tf_none); + tree t = get_normalized_constraints (CI_ASSOCIATED_CONSTRAINTS (ci), + args, info); + --processing_template_decl; + + return t; +} + +/* Returns the normalized constraints for the declaration D. */ + +static tree +get_normalized_constraints_from_decl (tree d, bool diag = false) +{ + tree tmpl; + tree decl; + + /* For inherited constructors, consider the original declaration; + it has the correct template information attached. */ + d = strip_inheriting_ctors (d); + + if (TREE_CODE (d) == TEMPLATE_DECL) + { + tmpl = d; + decl = DECL_TEMPLATE_RESULT (tmpl); + } + else + { + if (tree ti = DECL_TEMPLATE_INFO (d)) + tmpl = TI_TEMPLATE (ti); + else + tmpl = NULL_TREE; + decl = d; } - return error_mark_node; + + /* Get the most general template for the declaration, and compute + arguments from that. This ensures that the arguments used for + normalization are always template parameters and not arguments + used for outer specializations. For example: + + template<typename T> + struct S { + template<typename U> requires C<T, U> void f(U); + }; + + S<int>::f(0); + + When we normalize the requirements for S<int>::f, we want the + arguments to be {T, U}, not {int, U}. One reason for this is that + accepting the latter causes the template parameter level of U + to be reduced in a way that makes it overly difficult substitute + concrete arguments (i.e., eventually {int, int} during satisfaction. */ + if (tmpl) + { + if (DECL_LANG_SPECIFIC(tmpl) && !DECL_TEMPLATE_SPECIALIZATION (tmpl)) + tmpl = most_general_template (tmpl); + } + + /* If we're not diagnosing errors, use cached constraints, if any. */ + if (!diag) + if (tree *p = hash_map_safe_get (normalized_map, tmpl)) + return *p; + + tree args = generic_targs_for (tmpl); + tree ci = get_constraints (decl); + tree norm = get_normalized_constraints_from_info (ci, args, tmpl, diag); + + if (!diag) + hash_map_safe_put<hm_ggc> (normalized_map, tmpl, norm); + + return norm; +} + +/* Returns the normal form of TMPL's definition. */ + +static tree +normalize_concept_definition (tree tmpl, bool diag = false) +{ + if (!diag) + if (tree *p = hash_map_safe_get (normalized_map, tmpl)) + return *p; + + gcc_assert (concept_definition_p (tmpl)); + if (OVL_P (tmpl)) + tmpl = OVL_FIRST (tmpl); + gcc_assert (TREE_CODE (tmpl) == TEMPLATE_DECL); + tree args = generic_targs_for (tmpl); + tree def = get_concept_definition (DECL_TEMPLATE_RESULT (tmpl)); + ++processing_template_decl; + norm_info info (tmpl, diag ? tf_norm : tf_none); + tree norm = get_normalized_constraints (def, args, info); + --processing_template_decl; + + if (!diag) + hash_map_safe_put<hm_ggc> (normalized_map, tmpl, norm); + + return norm; +} + +/* Returns the normal form of TMPL's requirements. */ + +static tree +normalize_template_requirements (tree tmpl, bool diag = false) +{ + return get_normalized_constraints_from_decl (tmpl, diag); +} + +/* Returns the normal form of TMPL's requirements. */ + +static tree +normalize_nontemplate_requirements (tree decl, bool diag = false) +{ + return get_normalized_constraints_from_decl (decl, diag); +} + +/* Normalize an EXPR as a constraint using ARGS. */ + +static tree +normalize_constraint_expression (tree expr, tree args, bool diag = false) +{ + if (!expr || expr == error_mark_node) + return expr; + ++processing_template_decl; + norm_info info (diag ? tf_norm : tf_none); + tree norm = get_normalized_constraints (expr, args, info); + --processing_template_decl; + return norm; } - +/* Normalize an EXPR as a constraint. */ + +static tree +normalize_constraint_expression (tree expr, bool diag = false) +{ + if (!expr || expr == error_mark_node) + return expr; + + /* For concept checks, use the supplied template arguments as those used + for normalization. Otherwise, there are no template arguments. */ + tree args; + if (concept_check_p (expr)) + { + tree id = unpack_concept_check (expr); + args = TREE_OPERAND (id, 1); + } + else + args = NULL_TREE; + + return normalize_constraint_expression (expr, args, diag); +} + +/* 17.4.1.2p2. Two constraints are identical if they are formed + from the same expression and the targets of the parameter mapping + are equivalent. */ + +bool +atomic_constraints_identical_p (tree t1, tree t2) +{ + gcc_assert (TREE_CODE (t1) == ATOMIC_CONSTR); + gcc_assert (TREE_CODE (t2) == ATOMIC_CONSTR); + + if (ATOMIC_CONSTR_EXPR (t1) != ATOMIC_CONSTR_EXPR (t2)) + return false; + + if (!parameter_mapping_equivalent_p (t1, t2)) + return false; + + return true; +} + +/* True if T1 and T2 are equivalent, meaning they have the same syntactic + structure and all corresponding constraints are identical. */ + +bool +constraints_equivalent_p (tree t1, tree t2) +{ + gcc_assert (CONSTR_P (t1)); + gcc_assert (CONSTR_P (t2)); + + if (TREE_CODE (t1) != TREE_CODE (t2)) + return false; + + switch (TREE_CODE (t1)) + { + case CONJ_CONSTR: + case DISJ_CONSTR: + if (!constraints_equivalent_p (TREE_OPERAND (t1, 0), TREE_OPERAND (t2, 0))) + return false; + if (!constraints_equivalent_p (TREE_OPERAND (t1, 1), TREE_OPERAND (t2, 1))) + return false; + break; + case ATOMIC_CONSTR: + if (!atomic_constraints_identical_p(t1, t2)) + return false; + break; + default: + gcc_unreachable (); + } + return true; +} + +/* Compute the hash value for T. */ + +hashval_t +hash_atomic_constraint (tree t) +{ + gcc_assert (TREE_CODE (t) == ATOMIC_CONSTR); + + /* Hash the identity of the expression. */ + hashval_t val = htab_hash_pointer (ATOMIC_CONSTR_EXPR (t)); + + /* Hash the targets of the parameter map. */ + tree p = ATOMIC_CONSTR_MAP (t); + while (p) + { + val = iterative_hash_template_arg (TREE_PURPOSE (p), val); + p = TREE_CHAIN (p); + } + + return val; +} + +namespace inchash +{ + +static void +add_constraint (tree t, hash& h) +{ + h.add_int(TREE_CODE (t)); + switch (TREE_CODE (t)) + { + case CONJ_CONSTR: + case DISJ_CONSTR: + add_constraint (TREE_OPERAND (t, 0), h); + add_constraint (TREE_OPERAND (t, 1), h); + break; + case ATOMIC_CONSTR: + h.merge_hash (hash_atomic_constraint (t)); + break; + default: + gcc_unreachable (); + } +} + +} + +/* Computes a hash code for the constraint T. */ + +hashval_t +iterative_hash_constraint (tree t, hashval_t val) +{ + gcc_assert (CONSTR_P (t)); + inchash::hash h (val); + inchash::add_constraint (t, h); + return h.end (); +} // -------------------------------------------------------------------------- // // Constraint Semantic Processing @@ -1094,21 +1041,21 @@ { if (!current_template_parms) return NULL_TREE; - tree tmpl_constr = TEMPLATE_PARM_CONSTRAINTS (current_template_parms); + tree tmpl_constr = TEMPLATE_PARMS_CONSTRAINTS (current_template_parms); return build_constraints (tmpl_constr, NULL_TREE); } -// If the recently parsed TYPE declares or defines a template or template -// specialization, get its corresponding constraints from the current -// template parameters and bind them to TYPE's declaration. +/* 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) + if (!type || type == error_mark_node || !CLASS_TYPE_P (type)) return type; - // An explicit class template specialization has no template - // parameters. + /* An explicit class template specialization has no template parameters. */ if (!current_template_parms) return type; @@ -1117,17 +1064,19 @@ 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. + /* 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; + error ("%qT does not match original declaration", type); + tree tmpl = CLASSTYPE_TI_TEMPLATE (type); + location_t loc = DECL_SOURCE_LOCATION (tmpl); + inform (loc, "original template declaration here"); + /* Fall through, so that we define the type anyway. */ } return type; } @@ -1136,17 +1085,14 @@ return type; } -namespace { - -// Create an empty constraint info block. -inline tree_constraint_info* +/* Create an empty constraint info block. */ + +static 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 @@ -1156,27 +1102,143 @@ this returns NULL_TREE, indicating an unconstrained declaration. */ tree -build_constraints (tree tmpl_reqs, tree decl_reqs) +build_constraints (tree tr, tree dr) { - gcc_assert (tmpl_reqs ? constraint_p (tmpl_reqs) : true); - gcc_assert (decl_reqs ? constraint_p (decl_reqs) : true); - - if (!tmpl_reqs && !decl_reqs) + if (!tr && !dr) 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); + ci->template_reqs = tr; + ci->declarator_reqs = dr; + ci->associated_constr = combine_constraint_expressions (tr, dr); return (tree)ci; } -namespace { +/* Add constraint RHS to the end of CONSTRAINT_INFO ci. */ + +tree +append_constraint (tree ci, tree rhs) +{ + tree tr = ci ? CI_TEMPLATE_REQS (ci) : NULL_TREE; + tree dr = ci ? CI_DECLARATOR_REQS (ci) : NULL_TREE; + dr = combine_constraint_expressions (dr, rhs); + if (ci) + { + CI_DECLARATOR_REQS (ci) = dr; + tree ac = combine_constraint_expressions (tr, dr); + CI_ASSOCIATED_CONSTRAINTS (ci) = ac; + } + else + ci = build_constraints (tr, dr); + return ci; +} + +/* A mapping from declarations to constraint information. */ + +static GTY ((cache)) decl_tree_cache_map *decl_constraints; + +/* Returns the template constraints of declaration T. If T is not + constrained, return NULL_TREE. Note that T must be non-null. */ + +tree +get_constraints (const_tree t) +{ + if (!flag_concepts) + return NULL_TREE; + if (!decl_constraints) + return NULL_TREE; + + gcc_assert (DECL_P (t)); + if (TREE_CODE (t) == TEMPLATE_DECL) + t = DECL_TEMPLATE_RESULT (t); + tree* found = decl_constraints->get (CONST_CAST_TREE (t)); + if (found) + return *found; + else + return NULL_TREE; +} + +/* Associate the given constraint information CI with the declaration + T. If T is a template, then the constraints are associated with + its underlying declaration. Don't build associations if CI is + NULL_TREE. */ + +void +set_constraints (tree t, tree ci) +{ + if (!ci) + return; + gcc_assert (t && flag_concepts); + if (TREE_CODE (t) == TEMPLATE_DECL) + t = DECL_TEMPLATE_RESULT (t); + bool found = hash_map_safe_put<hm_ggc> (decl_constraints, t, ci); + gcc_assert (!found); +} + +/* Remove the associated constraints of the declaration T. */ + +void +remove_constraints (tree t) +{ + gcc_assert (DECL_P (t)); + if (TREE_CODE (t) == TEMPLATE_DECL) + t = DECL_TEMPLATE_RESULT (t); + + if (decl_constraints) + decl_constraints->remove (t); +} + +/* If DECL is a friend, substitute into REQS to produce requirements suitable + for declaration matching. */ + +tree +maybe_substitute_reqs_for (tree reqs, const_tree decl_) +{ + if (reqs == NULL_TREE) + return NULL_TREE; + tree decl = CONST_CAST_TREE (decl_); + tree result = STRIP_TEMPLATE (decl); + if (DECL_FRIEND_P (result)) + { + tree tmpl = decl == result ? DECL_TI_TEMPLATE (result) : decl; + tree gargs = generic_targs_for (tmpl); + processing_template_decl_sentinel s; + if (uses_template_parms (gargs)) + ++processing_template_decl; + reqs = tsubst_constraint (reqs, gargs, + tf_warning_or_error, NULL_TREE); + } + return reqs; +} + +/* Returns the template-head requires clause for the template + declaration T or NULL_TREE if none. */ + +tree +get_template_head_requirements (tree t) +{ + tree ci = get_constraints (t); + if (!ci) + return NULL_TREE; + return CI_TEMPLATE_REQS (ci); +} + +/* Returns the trailing requires clause of the declarator of + a template declaration T or NULL_TREE if none. */ + +tree +get_trailing_function_requirements (tree t) +{ + tree ci = get_constraints (t); + if (!ci) + return NULL_TREE; + return CI_DECLARATOR_REQS (ci); +} /* Construct a sequence of template arguments by prepending ARG to REST. Either ARG or REST may be null. */ -tree +static tree build_concept_check_arguments (tree arg, tree rest) { gcc_assert (rest ? TREE_CODE (rest) == TREE_VEC : true); @@ -1200,27 +1262,156 @@ 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. */ +/* Builds an id-expression of the form `C<Args...>()` where C is a function + concept. */ + +static tree +build_function_check (tree tmpl, tree args, tsubst_flags_t /*complain*/) +{ + if (TREE_CODE (tmpl) == TEMPLATE_DECL) + { + /* If we just got a template, wrap it in an overload so it looks like any + other template-id. */ + tmpl = ovl_make (tmpl); + TREE_TYPE (tmpl) = boolean_type_node; + } + + /* Perform function concept resolution now so we always have a single + function of the overload set (even if we started with only one; the + resolution function converts template arguments). Note that we still + wrap this in an overload set so we don't upset other parts of the + compiler that expect template-ids referring to function concepts + to have an overload set. */ + tree info = resolve_function_concept_overload (tmpl, args); + if (info == error_mark_node) + return error_mark_node; + if (!info) + { + error ("no matching concepts for %qE", tmpl); + return error_mark_node; + } + args = TREE_PURPOSE (info); + tmpl = DECL_TI_TEMPLATE (TREE_VALUE (info)); + + /* Rebuild the singleton overload set; mark the type bool. */ + tmpl = ovl_make (tmpl, NULL_TREE); + TREE_TYPE (tmpl) = boolean_type_node; + + /* Build the id-expression around the overload set. */ + tree id = build2 (TEMPLATE_ID_EXPR, boolean_type_node, tmpl, args); + + /* Finally, build the call expression around the overload. */ + ++processing_template_decl; + vec<tree, va_gc> *fargs = make_tree_vector (); + tree call = build_min_nt_call_vec (id, fargs); + TREE_TYPE (call) = boolean_type_node; + release_tree_vector (fargs); + --processing_template_decl; + + return call; +} + +/* Builds an id-expression of the form `C<Args...>` where C is a variable + concept. */ + +static tree +build_variable_check (tree tmpl, tree args, tsubst_flags_t complain) +{ + gcc_assert (variable_concept_p (tmpl)); + gcc_assert (TREE_CODE (tmpl) == TEMPLATE_DECL); + tree parms = INNERMOST_TEMPLATE_PARMS (DECL_TEMPLATE_PARMS (tmpl)); + args = coerce_template_parms (parms, args, tmpl, complain); + if (args == error_mark_node) + return error_mark_node; + return build2 (TEMPLATE_ID_EXPR, boolean_type_node, tmpl, args); +} + +/* Builds an id-expression of the form `C<Args...>` where C is a standard + concept. */ + +static tree +build_standard_check (tree tmpl, tree args, tsubst_flags_t complain) +{ + gcc_assert (standard_concept_p (tmpl)); + gcc_assert (TREE_CODE (tmpl) == TEMPLATE_DECL); + tree parms = INNERMOST_TEMPLATE_PARMS (DECL_TEMPLATE_PARMS (tmpl)); + args = coerce_template_parms (parms, args, tmpl, complain); + if (args == error_mark_node) + return error_mark_node; + return build2 (TEMPLATE_ID_EXPR, boolean_type_node, tmpl, args); +} + +/* Construct an expression that checks TARGET using ARGS. */ + tree -build_concept_check (tree target, tree arg, tree rest) +build_concept_check (tree target, tree args, tsubst_flags_t complain) { + return build_concept_check (target, NULL_TREE, args, complain); +} + +/* Construct an expression that checks the concept given by DECL. If + concept_definition_p (DECL) is false, this returns null. */ + +tree +build_concept_check (tree decl, tree arg, tree rest, tsubst_flags_t complain) +{ + if (arg == NULL_TREE && rest == NULL_TREE) + { + tree id = build_nt (TEMPLATE_ID_EXPR, decl, rest); + error ("invalid use concept %qE", id); + return error_mark_node; + } + 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)); + + if (standard_concept_p (decl)) + return build_standard_check (decl, args, complain); + if (variable_concept_p (decl)) + return build_variable_check (decl, args, complain); + if (function_concept_p (decl)) + return build_function_check (decl, args, complain); + + return error_mark_node; +} + +/* Build a template-id that can participate in a concept check. */ + +static tree +build_concept_id (tree decl, tree args) +{ + tree check = build_concept_check (decl, args, tf_warning_or_error); + if (check == error_mark_node) + return error_mark_node; + return unpack_concept_check (check); } +/* Build a template-id that can participate in a concept check, preserving + the source location of the original template-id. */ + +tree +build_concept_id (tree expr) +{ + gcc_assert (TREE_CODE (expr) == TEMPLATE_ID_EXPR); + tree id = build_concept_id (TREE_OPERAND (expr, 0), TREE_OPERAND (expr, 1)); + protected_set_expr_location (id, cp_expr_location (expr)); + return id; +} + +/* Build as template-id with a placeholder that can be used as a + type constraint. + + Note that this will diagnose errors if the initial concept check + cannot be built. */ + +tree +build_type_constraint (tree decl, tree args, tsubst_flags_t complain) +{ + tree wildcard = build_nt (WILDCARD_DECL); + tree check = build_concept_check (decl, wildcard, args, complain); + if (check == error_mark_node) + return error_mark_node; + return unpack_concept_check (check); +} /* Returns a TYPE_DECL that contains sufficient information to build a template parameter of the same kind as PROTO and @@ -1241,17 +1432,14 @@ 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. */ +/* Create a constraint expression for the given DECL that evaluates the + requirements specified by CONSTR, a TYPE_DECL that contains all the + information necessary to build the requirements (see finish_concept_name + for the layout of that TYPE_DECL). + + Note that the constraints are neither reduced nor decomposed. That is + done only after the requires clause has been parsed (or not). */ + tree finish_shorthand_constraint (tree decl, tree constr) { @@ -1266,41 +1454,46 @@ 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; + /* The TS lets use shorthand to constrain a pack of arguments, but the + standard does not. + + For the TS, consider: + + template<C... Ts> struct s; + + If C is variadic (and because Ts is a pack), we associate the + constraint C<Ts...>. In all other cases, we associate + the constraint (C<Ts> && ...). + + The standard behavior cannot be overridden by -fconcepts-ts. */ + bool variadic_concept_p = template_parameter_pack_p (proto); + bool declared_pack_p = template_parameter_pack_p (decl); + bool apply_to_each_p = (cxx_dialect >= cxx2a) ? true : !variadic_concept_p; /* Get the argument and overload used for the requirement and adjust it if we're going to expand later. */ tree arg = template_parm_to_arg (build_tree_list (NULL_TREE, decl)); - if (apply_to_all_p) + if (apply_to_each_p && declared_pack_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. */ + /* Build the concept constraint-expression. */ 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); + tree check = tmpl; + if (TREE_CODE (con) == FUNCTION_DECL) + check = ovl_make (tmpl); + check = build_concept_check (check, arg, args, tf_warning_or_error); + + /* Make the check a fold-expression if needed. */ + if (apply_to_each_p && declared_pack_p) + check = finish_left_unary_fold_expr (check, TRUTH_ANDIF_EXPR); + + return check; } /* Returns a conjunction of shorthand requirements for the template parameter list PARMS. Note that the requirements are stored in the TYPE of each tree node. */ + tree get_shorthand_constraints (tree parms) { @@ -1310,94 +1503,219 @@ { tree parm = TREE_VEC_ELT (parms, i); tree constr = TEMPLATE_PARM_CONSTRAINTS (parm); - result = conjoin_constraints (result, constr); + result = combine_constraint_expressions (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. +/* Get the deduced wildcard from a DEDUCED placeholder. If the deduced + wildcard is a pack, return the first argument of that pack. */ + static tree -process_introduction_parm (tree parameter_list, tree src_parm) +get_deduced_wildcard (tree wildcard) +{ + if (ARGUMENT_PACK_P (wildcard)) + wildcard = TREE_VEC_ELT (ARGUMENT_PACK_ARGS (wildcard), 0); + gcc_assert (TREE_CODE (wildcard) == WILDCARD_DECL); + return wildcard; +} + +/* Returns the prototype parameter for the nth deduced wildcard. */ + +static tree +get_introduction_prototype (tree wildcards, int index) +{ + return TREE_TYPE (get_deduced_wildcard (TREE_VEC_ELT (wildcards, index))); +} + +/* Introduce a type template parameter. */ + +static tree +introduce_type_template_parameter (tree wildcard, bool& non_type_p) { - // 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); + non_type_p = false; + return finish_template_type_parm (class_type_node, DECL_NAME (wildcard)); +} + +/* Introduce a template template parameter. */ + +static tree +introduce_template_template_parameter (tree wildcard, bool& non_type_p) +{ + non_type_p = false; + begin_template_parm_list (); + current_template_parms = DECL_TEMPLATE_PARMS (TREE_TYPE (wildcard)); + end_template_parm_list (); + return finish_template_template_parm (class_type_node, DECL_NAME (wildcard)); +} + +/* Introduce a template non-type parameter. */ + +static tree +introduce_nontype_template_parameter (tree wildcard, bool& non_type_p) +{ + non_type_p = true; + tree parm = copy_decl (TREE_TYPE (wildcard)); + DECL_NAME (parm) = DECL_NAME (wildcard); + return parm; +} + +/* Introduce a single template parameter. */ + +static tree +build_introduced_template_parameter (tree wildcard, bool& non_type_p) +{ + tree proto = TREE_TYPE (wildcard); tree parm; - 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) + if (TREE_CODE (proto) == TYPE_DECL) + parm = introduce_type_template_parameter (wildcard, non_type_p); + else if (TREE_CODE (proto) == TEMPLATE_DECL) + parm = introduce_template_template_parameter (wildcard, non_type_p); + else + parm = introduce_nontype_template_parameter (wildcard, non_type_p); + + /* Wrap in a TREE_LIST for process_template_parm. Note that introduced + parameters do not retain the defaults from the source parameter. */ + return build_tree_list (NULL_TREE, parm); +} + +/* Introduce a single template parameter. */ + +static tree +introduce_template_parameter (tree parms, tree wildcard) +{ + gcc_assert (!ARGUMENT_PACK_P (wildcard)); + tree proto = TREE_TYPE (wildcard); + location_t loc = DECL_SOURCE_LOCATION (wildcard); + + /* Diagnose the case where we have C{...Args}. */ + if (WILDCARD_PACK_P (wildcard)) { - 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; + tree id = DECL_NAME (wildcard); + error_at (loc, "%qE cannot be introduced with an ellipsis %<...%>", id); + inform (DECL_SOURCE_LOCATION (proto), "prototype declared here"); } - // 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); + bool non_type_p; + tree parm = build_introduced_template_parameter (wildcard, non_type_p); + return process_template_parm (parms, loc, parm, non_type_p, false); +} + +/* Introduce a template parameter pack. */ + +static tree +introduce_template_parameter_pack (tree parms, tree wildcard) +{ + bool non_type_p; + tree parm = build_introduced_template_parameter (wildcard, non_type_p); + location_t loc = DECL_SOURCE_LOCATION (wildcard); + return process_template_parm (parms, loc, parm, non_type_p, true); +} + +/* Introduce the nth template parameter. */ + +static tree +introduce_template_parameter (tree parms, tree wildcards, int& index) +{ + tree deduced = TREE_VEC_ELT (wildcards, index++); + return introduce_template_parameter (parms, deduced); +} + +/* Introduce either a template parameter pack or a list of template + parameters. */ + +static tree +introduce_template_parameters (tree parms, tree wildcards, int& index) +{ + /* If the prototype was a parameter, we better have deduced an + argument pack, and that argument must be the last deduced value + in the wildcard vector. */ + tree deduced = TREE_VEC_ELT (wildcards, index++); + gcc_assert (ARGUMENT_PACK_P (deduced)); + gcc_assert (index == TREE_VEC_LENGTH (wildcards)); + + /* Introduce each element in the pack. */ + tree args = ARGUMENT_PACK_ARGS (deduced); + for (int i = 0; i < TREE_VEC_LENGTH (args); ++i) + { + tree arg = TREE_VEC_ELT (args, i); + if (WILDCARD_PACK_P (arg)) + parms = introduce_template_parameter_pack (parms, arg); + else + parms = introduce_template_parameter (parms, arg); + } + + return parms; } -/* 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. */ +/* Builds the template parameter list PARMS by chaining introduced + parameters from the WILDCARD vector. INDEX is the position of + the current parameter. */ + +static tree +process_introduction_parms (tree parms, tree wildcards, int& index) +{ + tree proto = get_introduction_prototype (wildcards, index); + if (template_parameter_pack_p (proto)) + return introduce_template_parameters (parms, wildcards, index); + else + return introduce_template_parameter (parms, wildcards, index); +} + +/* Ensure that all template parameters have been introduced for the concept + named in CHECK. If not, emit a diagnostic. + + Note that implicitly introducing a parameter with a default argument + creates a case where a parameter is declared, but unnamed, making + it unusable in the definition. */ + +static bool +check_introduction_list (tree intros, tree check) +{ + check = unpack_concept_check (check); + tree tmpl = TREE_OPERAND (check, 0); + if (OVL_P (tmpl)) + tmpl = OVL_FIRST (tmpl); + + tree parms = DECL_INNERMOST_TEMPLATE_PARMS (tmpl); + if (TREE_VEC_LENGTH (intros) < TREE_VEC_LENGTH (parms)) + { + error_at (input_location, "all template parameters of %qD must " + "be introduced", tmpl); + return false; + } + + return true; +} + +/* Associates a constraint check to the current template based on the + introduction parameters. INTRO_LIST must be a TREE_VEC of WILDCARD_DECLs + containing a chained PARM_DECL which contains the identifier as well as + the source location. TMPL_DECL is the decl for the concept being used. + If we take a concept, C, this will form a check in the form of + C<INTRO_LIST> filling in any extra arguments needed by the defaults + deduced. + + Returns NULL_TREE if no concept could be matched and error_mark_node if + an error occurred when matching. */ + tree -finish_template_introduction (tree tmpl_decl, tree intro_list) +finish_template_introduction (tree tmpl_decl, + tree intro_list, + location_t intro_loc) { - /* Deduce the concept check. */ - tree expr = build_concept_check (tmpl_decl, NULL_TREE, intro_list); + /* Build a concept check to deduce the actual parameters. */ + tree expr = build_concept_check (tmpl_decl, intro_list, tf_none); if (expr == error_mark_node) - return NULL_TREE; + { + error_at (intro_loc, "cannot deduce template parameters from " + "introduction list"); + return error_mark_node; + } + + if (!check_introduction_list (intro_list, expr)) + return error_mark_node; tree parms = deduce_concept_introduction (expr); if (!parms) @@ -1407,9 +1725,15 @@ 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)); + for (int n = 0; n < nargs; ) + parm_list = process_introduction_parms (parm_list, parms, n); parm_list = end_template_parm_list (parm_list); + + /* Update the number of arguments to reflect the number of deduced + template parameter introductions. */ + nargs = TREE_VEC_LENGTH (parm_list); + + /* Determine if any errors occurred during matching. */ for (int i = 0; i < TREE_VEC_LENGTH (parm_list); ++i) if (TREE_VALUE (TREE_VEC_ELT (parm_list, i)) == error_mark_node) { @@ -1418,7 +1742,7 @@ } /* Build a concept check for our constraint. */ - tree check_args = make_tree_vec (TREE_VEC_LENGTH (parms)); + tree check_args = make_tree_vec (nargs); int n = 0; for (; n < TREE_VEC_LENGTH (parm_list); ++n) { @@ -1432,22 +1756,33 @@ 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; + /* Associate the constraint. */ + tree check = build_concept_check (tmpl_decl, + check_args, + tf_warning_or_error); + TEMPLATE_PARMS_CONSTRAINTS (current_template_parms) = check; return parm_list; } -/* Given the predicate constraint T from a constrained-type-specifier, extract +/* Given the concept check T from a constrained-type-specifier, extract its TMPL and ARGS. FIXME why do we need two different forms of constrained-type-specifier? */ void placeholder_extract_concept_and_args (tree t, tree &tmpl, tree &args) { + if (concept_check_p (t)) + { + t = unpack_concept_check (t); + tmpl = TREE_OPERAND (t, 0); + if (TREE_CODE (tmpl) == OVERLOAD) + tmpl = OVL_FIRST (tmpl); + args = TREE_OPERAND (t, 1); + return; + } + if (TREE_CODE (t) == TYPE_DECL) { /* A constrained parameter. Build a constraint check @@ -1458,20 +1793,10 @@ 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. */ + and C2 can be either TEMPLATE_TYPE_PARM or template-ids. */ bool equivalent_placeholder_constraints (tree c1, tree c2) @@ -1516,7 +1841,7 @@ return true; } -/* Return a hash value for the placeholder PRED_CONSTR C. */ +/* Return a hash value for the placeholder ATOMIC_CONSTR C. */ hashval_t hash_placeholder_constraint (tree c) @@ -1533,178 +1858,198 @@ 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) +/* Substitute through the simple requirement. */ + +static tree +tsubst_valid_expression_requirement (tree t, tree args, subst_info info) { - 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); + return tsubst_expr (t, args, info.complain, info.in_decl, false); } -/* Substitute into a check constraint. */ - -tree -tsubst_check_constraint (tree t, tree args, - tsubst_flags_t complain, tree in_decl) + +/* Substitute through the simple requirement. */ + +static tree +tsubst_simple_requirement (tree t, tree args, subst_info info) { - 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) + tree t0 = TREE_OPERAND (t, 0); + tree expr = tsubst_valid_expression_requirement (t0, args, info); + if (expr == 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); + return finish_simple_requirement (EXPR_LOCATION (t), expr); } -/* 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) +/* Substitute through the type requirement. */ + +static tree +tsubst_type_requirement (tree t, tree args, subst_info info) { 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) + tree type = tsubst (t0, args, info.complain, info.in_decl); + if (type == 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); + return finish_type_requirement (EXPR_LOCATION (t), 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) +/* True if TYPE can be deduced from EXPR. */ + +static bool +type_deducible_p (tree expr, tree type, tree placeholder, tree args, + subst_info info) { - 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) + /* Make sure deduction is performed against ( EXPR ), so that + references are preserved in the result. */ + expr = force_paren_expr_uneval (expr); + + /* Replace the constraints with the instantiated constraints. This + substitutes args into any template parameters in the trailing + result type. */ + tree saved_constr = PLACEHOLDER_TYPE_CONSTRAINTS (placeholder); + tree subst_constr + = tsubst_constraint (saved_constr, + args, + info.complain | tf_partial, + info.in_decl); + + if (subst_constr == error_mark_node) + return false; + + PLACEHOLDER_TYPE_CONSTRAINTS (placeholder) = subst_constr; + + /* Temporarily unlink the canonical type. */ + tree saved_type = TYPE_CANONICAL (placeholder); + TYPE_CANONICAL (placeholder) = NULL_TREE; + + tree deduced_type + = do_auto_deduction (type, + expr, + placeholder, + info.complain, + adc_requirement); + + PLACEHOLDER_TYPE_CONSTRAINTS (placeholder) = saved_constr; + TYPE_CANONICAL (placeholder) = saved_type; + + if (deduced_type == error_mark_node) + return false; + + return true; +} + +/* True if EXPR can not be converted to TYPE. */ + +static bool +expression_convertible_p (tree expr, tree type, subst_info info) +{ + tree conv = + perform_direct_initialization_if_possible (type, expr, false, + info.complain); + if (conv == error_mark_node) + return false; + if (conv == NULL_TREE) + { + if (info.complain & tf_error) + { + location_t loc = EXPR_LOC_OR_LOC (expr, input_location); + error_at (loc, "cannot convert %qE to %qT", expr, type); + } + return false; + } + return true; +} + + +/* Substitute through the compound requirement. */ + +static tree +tsubst_compound_requirement (tree t, tree args, subst_info info) +{ + tree t0 = TREE_OPERAND (t, 0); + tree t1 = TREE_OPERAND (t, 1); + tree expr = tsubst_valid_expression_requirement (t0, args, info); + if (expr == error_mark_node) return error_mark_node; - /* 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) + + /* Check the noexcept condition. */ + bool noexcept_p = COMPOUND_REQ_NOEXCEPT_P (t); + if (noexcept_p && !expr_noexcept_p (expr, tf_none)) + return error_mark_node; + + /* Substitute through the type expression, if any. */ + tree type = tsubst (t1, args, info.complain, info.in_decl); + if (type == error_mark_node) return error_mark_node; - return build_nt (DEDUCT_CONSTR, new_expr, new_pattern, autos); + + /* Check expression against the result type. */ + if (type) + { + if (tree placeholder = type_uses_auto (type)) + { + if (!type_deducible_p (expr, type, placeholder, args, info)) + return error_mark_node; + } + else if (!expression_convertible_p (expr, type, info)) + return error_mark_node; + } + + return finish_compound_requirement (EXPR_LOCATION (t), + expr, type, noexcept_p); +} + +static tree +tsubst_nested_requirement (tree t, tree args, subst_info info) +{ + gcc_assert (!uses_template_parms (args)); + + /* Ensure that we're in an evaluation context prior to satisfaction. */ + tree norm = TREE_VALUE (TREE_TYPE (t)); + tree result = satisfy_constraint (norm, args, info); + if (result != boolean_true_node) + return error_mark_node; + return result; } -/* Substitute ARGS into the exception constraint T. */ - -tree -tsubst_exception_constr (tree t, tree args, tsubst_flags_t complain, - tree in_decl) +/* Substitute ARGS into the requirement T. */ + +static tree +tsubst_requirement (tree t, tree args, subst_info info) { - 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); + iloc_sentinel loc_s (cp_expr_location (t)); + switch (TREE_CODE (t)) + { + case SIMPLE_REQ: + return tsubst_simple_requirement (t, args, info); + case TYPE_REQ: + return tsubst_type_requirement (t, args, info); + case COMPOUND_REQ: + return tsubst_compound_requirement (t, args, info); + case NESTED_REQ: + return tsubst_nested_requirement (t, args, info); + default: + break; + } + gcc_unreachable (); } -/* 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 +/* Substitute ARGS into the list of requirements T. Note that + substitution failures here result in ill-formed programs. */ + +static tree +tsubst_requirement_body (tree t, tree args, subst_info info) +{ + tree result = NULL_TREE; + while (t) + { + tree req = tsubst_requirement (TREE_VALUE (t), args, info); + if (req == error_mark_node) + return error_mark_node; + result = tree_cons (NULL_TREE, req, result); + t = TREE_CHAIN (t); + } + return nreverse (result); +} + +static tree declare_constraint_vars (tree parms, tree vars) { tree s = vars; @@ -1724,6 +2069,24 @@ return vars; } +/* Substitute through as if checking function parameter types. This + will diagnose common parameter type errors. Returns error_mark_node + if an error occurred. */ + +static tree +check_constaint_variables (tree t, tree args, subst_info info) +{ + tree types = NULL_TREE; + tree p = t; + while (p && !VOID_TYPE_P (p)) + { + types = tree_cons (NULL_TREE, TREE_TYPE (p), types); + p = TREE_CHAIN (p); + } + types = chainon (nreverse (types), void_list_node); + return tsubst_function_parms (types, args, info.complain, info.in_decl); +} + /* A subroutine of tsubst_parameterized_constraint. Substitute ARGS into the parameter list T, producing a sequence of constraint variables, declared in the current scope. @@ -1732,171 +2095,81 @@ 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) +static tree +tsubst_constraint_variables (tree t, tree args, subst_info info) { + /* Perform a trial substitution to check for type errors. */ + tree parms = check_constaint_variables (t, args, info); + if (parms == error_mark_node) + return error_mark_node; + /* Clear cp_unevaluated_operand across tsubst so that we get a proper chain of PARM_DECLs. */ int saved_unevaluated_operand = cp_unevaluated_operand; cp_unevaluated_operand = 0; - tree vars = tsubst (t, args, complain, in_decl); + tree vars = tsubst (t, args, info.complain, info.in_decl); cp_unevaluated_operand = saved_unevaluated_operand; if (vars == error_mark_node) return error_mark_node; return declare_constraint_vars (t, vars); } -/* Substitute ARGS into the 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. */ +/* Substitute ARGS into the requires-expression T. [8.4.7]p6. The + substitution of template arguments into a requires-expression + may result in the formation of invalid types or expressions + in its requirements ... In such cases, the expression evaluates + to false; it does not cause the program to be ill-formed. + + However, there are cases where substitution must produce a + new requires-expression, that is not a template constraint. + For example: + + template<typename T> + class X { + template<typename U> + static constexpr bool var = requires (U u) { T::fn(u); }; + }; + + In the instantiation of X<Y> (assuming Y defines fn), then the + instantiated requires-expression would include Y::fn(u). If any + substitution in the requires-expression fails, we can immediately + fold the expression to false, as would be the case e.g., when + instantiation X<int>. */ tree tsubst_requires_expr (tree t, tree args, - tsubst_flags_t complain, tree in_decl) + tsubst_flags_t complain, tree in_decl) { - local_specialization_stack stack; + local_specialization_stack stack (lss_copy); + + subst_info info (complain, in_decl); + + /* A requires-expression is an unevaluated context. */ + cp_unevaluated u; tree parms = TREE_OPERAND (t, 0); if (parms) { - parms = tsubst_constraint_variables (parms, args, complain, in_decl); + parms = tsubst_constraint_variables (parms, args, info); if (parms == error_mark_node) - return error_mark_node; + return boolean_false_node; } tree reqs = TREE_OPERAND (t, 1); - reqs = tsubst_requirement_body (reqs, args, complain, in_decl); + reqs = tsubst_requirement_body (reqs, args, info); if (reqs == error_mark_node) - return error_mark_node; - - return finish_requires_expr (parms, reqs); + return boolean_false_node; + + /* In certain cases, produce a new requires-expression. + Otherwise the value of the expression is true. */ + if (processing_template_decl && uses_template_parms (args)) + return finish_requires_expr (cp_expr_location (t), parms, reqs); + + return boolean_true_node; } /* Substitute ARGS into the constraint information CI, producing a new - constraint record. */ + constraint record. */ tree tsubst_constraint_info (tree t, tree args, @@ -1905,378 +2178,414 @@ 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); + tree tr = tsubst_constraint (CI_TEMPLATE_REQS (t), args, complain, in_decl); + tree dr = tsubst_constraint (CI_DECLARATOR_REQS (t), args, complain, in_decl); + return build_constraints (tr, dr); } -/* Substitute ARGS into the constraint T. */ +/* Substitute through a parameter mapping, in order to get the actual + arguments used to instantiate an atomic constraint. This may fail + if the substitution into arguments produces something ill-formed. */ + +static tree +tsubst_parameter_mapping (tree map, tree args, subst_info info) +{ + if (!map) + return NULL_TREE; + + tsubst_flags_t complain = info.complain; + tree in_decl = info.in_decl; + + tree result = NULL_TREE; + for (tree p = map; p; p = TREE_CHAIN (p)) + { + if (p == error_mark_node) + return error_mark_node; + tree parm = TREE_VALUE (p); + tree arg = TREE_PURPOSE (p); + tree new_arg = NULL_TREE; + if (TYPE_P (arg)) + { + /* If a template parameter is declared with a placeholder, we can + get those in the argument list if decltype is applied to the + placeholder. For example: + + template<auto T> + requires C<decltype(T)> + void f() { } + + The normalized argument for C will be an auto type, so we'll + need to deduce the actual argument from the corresponding + initializer (whatever argument is provided for T), and use + that result in the instantiated parameter mapping. */ + if (tree auto_node = type_uses_auto (arg)) + { + int level; + int index; + template_parm_level_and_index (parm, &level, &index); + tree init = TMPL_ARG (args, level, index); + new_arg = do_auto_deduction (arg, init, auto_node, + complain, adc_variable_type, + make_tree_vec (0)); + } + } + else if (ARGUMENT_PACK_P (arg)) + new_arg = tsubst_argument_pack (arg, args, complain, in_decl); + if (!new_arg) + new_arg = tsubst_template_arg (arg, args, complain, in_decl); + if (new_arg == error_mark_node) + return error_mark_node; + + result = tree_cons (new_arg, parm, result); + } + return nreverse (result); +} tree -tsubst_constraint (tree t, tree args, tsubst_flags_t complain, tree in_decl) +tsubst_parameter_mapping (tree map, tree args, tsubst_flags_t complain, tree in_decl) { - if (t == NULL_TREE || t == error_mark_node) - return t; - switch (TREE_CODE (t)) - { - case PRED_CONSTR: - return tsubst_predicate_constraint (t, args, complain, in_decl); - case CHECK_CONSTR: - return tsubst_check_constraint (t, args, complain, in_decl); - case CONJ_CONSTR: - case DISJ_CONSTR: - return tsubst_logical_operator (t, args, complain, in_decl); - case PARM_CONSTR: - return tsubst_parameterized_constraint (t, args, complain, in_decl); - case EXPR_CONSTR: - return tsubst_expr_constr (t, args, complain, in_decl); - case TYPE_CONSTR: - return tsubst_type_constr (t, args, complain, in_decl); - case ICONV_CONSTR: - return tsubst_implicit_conversion_constr (t, args, complain, in_decl); - case DEDUCT_CONSTR: - return tsubst_argument_deduction_constr (t, args, complain, in_decl); - case EXCEPT_CONSTR: - return tsubst_exception_constr (t, args, complain, in_decl); - default: - gcc_unreachable (); - } - return error_mark_node; + return tsubst_parameter_mapping (map, args, subst_info (complain, in_decl)); } /*--------------------------------------------------------------------------- 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) +/* Hash functions for satisfaction entries. */ + +struct GTY((for_user)) sat_entry +{ + tree constr; + tree args; + tree result; +}; + +struct sat_hasher : ggc_ptr_hash<sat_entry> +{ + static hashval_t hash (sat_entry *e) + { + hashval_t value = hash_atomic_constraint (e->constr); + return iterative_hash_template_arg (e->args, value); + } + + static bool equal (sat_entry *e1, sat_entry *e2) + { + if (!atomic_constraints_identical_p (e1->constr, e2->constr)) + return false; + return template_args_equal (e1->args, e2->args); + } +}; + +/* Cache the result of satisfy_atom. */ +static GTY((deletable)) hash_table<sat_hasher> *sat_cache; + +/* Cache the result of constraint_satisfaction_value. */ +static GTY((deletable)) hash_map<tree, tree> *decl_satisfied_cache; + +static tree +get_satisfaction (tree constr, tree args) +{ + if (!sat_cache) + return NULL_TREE; + sat_entry elt = { constr, args, NULL_TREE }; + sat_entry* found = sat_cache->find (&elt); + if (found) + return found->result; + else + return NULL_TREE; +} + +static void +save_satisfaction (tree constr, tree args, tree result) { - /* 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; + if (!sat_cache) + sat_cache = hash_table<sat_hasher>::create_ggc (31); + sat_entry elt = {constr, args, result}; + sat_entry** slot = sat_cache->find_slot (&elt, INSERT); + sat_entry* entry = ggc_alloc<sat_entry> (); + *entry = elt; + *slot = entry; +} + +void +clear_satisfaction_cache () +{ + if (sat_cache) + sat_cache->empty (); + if (decl_satisfied_cache) + decl_satisfied_cache->empty (); } -/* 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. */ +/* A tool to help manage satisfaction caching in satisfy_constraint_r. + Note the cache is only used when not diagnosing errors. */ + +struct satisfaction_cache +{ + satisfaction_cache (tree constr, tree args, tsubst_flags_t complain) + : constr(constr), args(args), complain(complain) + { } + + tree get () + { + if (complain == tf_none) + return get_satisfaction (constr, args); + return NULL_TREE; + } + + tree save (tree result) + { + if (complain == tf_none) + save_satisfaction (constr, args, result); + return result; + } + + tree constr; + tree args; + tsubst_flags_t complain; +}; + +static int satisfying_constraint = 0; + +/* Returns true if we are currently satisfying a constraint. + + This is used to guard against recursive calls to evaluate_concept_check + during template argument substitution. + + TODO: Do we need this now that we fully normalize prior to evaluation? + I think not. */ + +bool +satisfying_constraint_p () +{ + return satisfying_constraint; +} + +/* Substitute ARGS into constraint-expression T during instantiation of + a member of a class template. */ tree -satisfy_predicate_constraint (tree t, tree args, - tsubst_flags_t complain, tree in_decl) +tsubst_constraint (tree t, tree args, tsubst_flags_t complain, tree in_decl) { - tree expr = TREE_OPERAND (t, 0); - - /* We should never have a naked pack expansion in a predicate constraint. */ - gcc_assert (TREE_CODE (expr) != EXPR_PACK_EXPANSION); - - /* If substitution into the expression fails, the constraint - is not satisfied. */ - expr = tsubst_expr (expr, args, complain, in_decl, false); - if (expr == error_mark_node) - return boolean_false_node; - - /* A predicate constraint shall have type bool. In some - cases, substitution gives us const-qualified bool, which - is also acceptable. */ - tree type = cv_unqualified (TREE_TYPE (expr)); - if (!same_type_p (type, boolean_type_node)) - { - error_at (cp_expr_loc_or_loc (expr, input_location), - "constraint %qE does not have type %qT", - expr, boolean_type_node); - return boolean_false_node; - } - - return cxx_constant_value (expr); + /* We also don't want to evaluate concept-checks when substituting the + constraint-expressions of a declaration. */ + processing_constraint_expression_sentinel s; + tree expr = tsubst_expr (t, args, complain, in_decl, false); + return 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) +static tree satisfy_constraint_r (tree, tree, subst_info info); + +/* Compute the satisfaction of a conjunction. */ + +static tree +satisfy_conjunction (tree t, tree args, subst_info info) { - 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); + tree lhs = satisfy_constraint_r (TREE_OPERAND (t, 0), args, info); + if (lhs == error_mark_node || lhs == boolean_false_node) + return lhs; + return satisfy_constraint_r (TREE_OPERAND (t, 1), args, info); } -/* 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) +/* Compute the satisfaction of a disjunction. */ + +static tree +satisfy_disjunction (tree t, tree args, subst_info info) { - 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; + /* Evaluate the operands quietly. */ + subst_info quiet (tf_none, NULL_TREE); + + /* Register the constraint for diagnostics, if needed. */ + diagnosing_failed_constraint failure (t, args, info.noisy ()); + + tree lhs = satisfy_constraint_r (TREE_OPERAND (t, 0), args, quiet); + if (lhs == boolean_true_node) + return boolean_true_node; + tree rhs = satisfy_constraint_r (TREE_OPERAND (t, 1), args, quiet); + if (rhs != boolean_true_node && info.noisy ()) + { + location_t loc = cp_expr_location (CONSTR_EXPR (t)); + inform (loc, "neither operand of the disjunction is satisfied"); + /* TODO: Replay the LHS and RHS to find failures in both branches. */ + // satisfy_constraint_r (TREE_OPERAND (t, 0), args, info); + // satisfy_constraint_r (TREE_OPERAND (t, 1), args, info); + } + return rhs; } -/* 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. */ +/* Ensures that T is a truth value and not (accidentally, as sometimes + happens) an integer value. */ tree -satisfy_implicit_conversion_constraint (tree t, tree args, - tsubst_flags_t complain, tree in_decl) +satisfaction_value (tree t) { - /* 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 + if (t == error_mark_node) + return t; + if (t == boolean_true_node || t == integer_one_node) 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) + if (t == boolean_false_node || t == integer_zero_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; + /* Anything else should be invalid. */ + gcc_assert (false); } -/* 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. */ +/* Build a new template argument list with template arguments corresponding + to the parameters used in an atomic constraint. */ tree -satisfy_parameterized_constraint (tree t, tree args, - tsubst_flags_t complain, tree in_decl) +get_mapped_args (tree map) { - 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); + /* No map, no arguments. */ + if (!map) + return NULL_TREE; + + /* Find the mapped parameter with the highest level. */ + int count = 0; + for (tree p = map; p; p = TREE_CHAIN (p)) + { + int level; + int index; + template_parm_level_and_index (TREE_VALUE (p), &level, &index); + if (level > count) + count = level; + } + + /* Place each argument at its corresponding position in the argument + list. Note that the list will be sparse (not all arguments supplied), + but instantiation is guaranteed to only use the parameters in the + mapping, so null arguments would never be used. */ + auto_vec< vec<tree> > lists (count); + lists.quick_grow_cleared (count); + for (tree p = map; p; p = TREE_CHAIN (p)) + { + int level; + int index; + template_parm_level_and_index (TREE_VALUE (p), &level, &index); + + /* Insert the argument into its corresponding position. */ + vec<tree> &list = lists[level - 1]; + if (index >= (int)list.length ()) + list.safe_grow_cleared (index + 1); + list[index] = TREE_PURPOSE (p); + } + + /* Build the new argument list. */ + tree args = make_tree_vec (lists.length ()); + for (unsigned i = 0; i != lists.length (); ++i) + { + vec<tree> &list = lists[i]; + tree level = make_tree_vec (list.length ()); + for (unsigned j = 0; j < list.length(); ++j) + TREE_VEC_ELT (level, j) = list[j]; + SET_TMPL_ARGS_LEVEL (args, i + 1, level); + list.release (); + } + SET_NON_DEFAULT_TEMPLATE_ARGS_COUNT (args, 0); + + return args; } -/* 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) +static void diagnose_atomic_constraint (tree, tree, subst_info); + +/* Compute the satisfaction of an atomic constraint. */ + +static tree +satisfy_atom (tree t, tree args, subst_info info) { - 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); + satisfaction_cache cache (t, args, info.complain); + if (tree r = cache.get ()) + return r; + + /* Perform substitution quietly. */ + subst_info quiet (tf_none, NULL_TREE); + + /* In case there is a diagnostic, we want to establish the context + prior to printing errors. If no errors occur, this context is + removed before returning. */ + diagnosing_failed_constraint failure (t, args, info.noisy ()); + + /* Instantiate the parameter mapping. */ + tree map = tsubst_parameter_mapping (ATOMIC_CONSTR_MAP (t), args, quiet); + if (map == error_mark_node) + { + /* If instantiation of the parameter mapping fails, the program + is ill-formed. */ + if (info.noisy()) + tsubst_parameter_mapping (ATOMIC_CONSTR_MAP (t), args, info); + return cache.save (boolean_false_node); + } + + /* Rebuild the argument vector from the parameter mapping. */ + args = get_mapped_args (map); + + /* Apply the parameter mapping (i.e., just substitute). */ + tree expr = ATOMIC_CONSTR_EXPR (t); + tree result = tsubst_expr (expr, args, quiet.complain, quiet.in_decl, false); + if (result == error_mark_node) + { + /* If substitution results in an invalid type or expression, the constraint + is not satisfied. Replay the substitution. */ + if (info.noisy ()) + tsubst_expr (expr, args, info.complain, info.in_decl, false); + return cache.save (boolean_false_node); + } + + location_t loc = cp_expr_loc_or_input_loc (expr); + + /* [17.4.1.2] ... lvalue-to-value conversion is performed as necessary, + and EXPR shall be a constant expression of type bool. */ + result = force_rvalue (result, info.complain); + if (result == error_mark_node) + return cache.save (error_mark_node); + if (!same_type_p (TREE_TYPE (result), boolean_type_node)) + { + if (info.noisy ()) + error_at (loc, "constraint does not have type %<bool%>"); + return cache.save (error_mark_node); + } + + /* Compute the value of the constraint. */ + result = satisfaction_value (cxx_constant_value (result)); + if (result == boolean_false_node && info.noisy ()) + diagnose_atomic_constraint (t, args, info); + + return cache.save (result); } -/* 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) +/* Determine if the normalized constraint T is satisfied. + Returns boolean_true_node if the expression/constraint is + satisfied, boolean_false_node if not, and error_mark_node + if the there was an error evaluating the constraint. + + The parameter mapping of atomic constraints is simply the + set of template arguments that will be substituted into + the expression, regardless of template parameters appearing + withing. Whether a template argument is used in the atomic + constraint only matters for subsumption. */ + +static tree +satisfy_constraint_r (tree t, tree args, subst_info info) { - 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; + return error_mark_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; + { + case CONJ_CONSTR: + return satisfy_conjunction (t, args, info); + case DISJ_CONSTR: + return satisfy_disjunction (t, args, info); + case ATOMIC_CONSTR: + return satisfy_atom (t, args, info); + default: + gcc_unreachable (); + } } -/* Check that the 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) +/* Check that the normalized constraint T is satisfied for ARGS. */ + +static tree +satisfy_constraint (tree t, tree args, subst_info info) { auto_timevar time (TV_CONSTRAINT_SAT); @@ -2284,155 +2593,208 @@ 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); + /* We need to check access during satisfaction. */ + deferring_access_check_sentinel acs (dk_no_deferred); + + return satisfy_constraint_r (t, args, info); } -/* 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) +/* Check the normalized constraints T against ARGS, returning a satisfaction + value (either true, false, or error). */ + +static tree +satisfy_associated_constraints (tree t, tree args, subst_info info) { - /* If there are no constraints then this is trivially satisfied. */ - if (!ci) + /* If there are no constraints then this is trivially satisfied. */ + if (!t) return boolean_true_node; /* If any arguments depend on template parameters, we can't - check constraints. */ + check constraints. Pretend they're satisfied for now. */ 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); + return satisfy_constraint (t, args, info); } -} /* 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) +/* Evaluate EXPR as a constraint expression using ARGS, returning a + satisfaction value. */ + +static tree +satisfy_constraint_expression (tree t, tree args, subst_info info) { - gcc_assert (constraint_p (constr)); - return satisfy_constraint (constr, args); + if (t == error_mark_node) + return error_mark_node; + + gcc_assert (EXPR_P (t)); + + /* Get the normalized constraints. */ + tree norm; + if (args == NULL_TREE && concept_check_p (t)) + { + tree id = unpack_concept_check (t); + args = TREE_OPERAND (id, 1); + tree tmpl = get_concept_check_template (id); + norm = normalize_concept_definition (tmpl, info.noisy ()); + } + else + norm = normalize_constraint_expression (t, info.noisy ()); + + /* Perform satisfaction. */ + return satisfy_constraint (norm, args, info); } -/* 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. */ +/* Used only to evaluate requires-expressions during constant expression + evaluation. */ tree -evaluate_function_concept (tree fn, tree args) +satisfy_constraint_expression (tree expr) { - 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); + subst_info info (tf_none, NULL_TREE); + return satisfy_constraint_expression (expr, NULL_TREE, info); } -/* 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) +static tree +satisfy_declaration_constraints (tree t, subst_info info) { - 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; + gcc_assert (DECL_P (t)); + + /* For inherited constructors, consider the original declaration; + it has the correct template information attached. */ + if (flag_new_inheriting_ctors) + t = strip_inheriting_ctors (t); + + /* Update the declaration for diagnostics. */ + info.in_decl = t; + + if (info.quiet ()) + if (tree *result = hash_map_safe_get (decl_satisfied_cache, t)) + return *result; + + /* Get the normalized constraints. */ + tree norm = NULL_TREE; tree args = NULL_TREE; - if (tree ti = DECL_TEMPLATE_INFO (decl)) + if (tree ti = DECL_TEMPLATE_INFO (t)) { 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); + norm = normalize_template_requirements (tmpl, info.noisy ()); + + /* The initial parameter mapping is the complete set of + template arguments substituted into the declaration. */ + args = TI_ARGS (ti); } else { - ci = get_constraints (decl); + /* These should be empty until we allow constraints on non-templates. */ + norm = normalize_nontemplate_requirements (t, info.noisy ()); + } + + tree result = boolean_true_node; + if (norm) + { + if (!push_tinst_level (t)) + return result; + push_access_scope (t); + result = satisfy_associated_constraints (norm, args, info); + pop_access_scope (t); + pop_tinst_level (); + } + + if (info.quiet ()) + hash_map_safe_put<hm_ggc> (decl_satisfied_cache, t, result); + + return result; +} + +static tree +satisfy_declaration_constraints (tree t, tree args, subst_info info) +{ + /* Update the declaration for diagnostics. */ + info.in_decl = t; + + gcc_assert (TREE_CODE (t) == TEMPLATE_DECL); + if (tree norm = normalize_template_requirements (t, info.noisy ())) + { + tree pattern = DECL_TEMPLATE_RESULT (t); + push_access_scope (pattern); + tree result = satisfy_associated_constraints (norm, args, info); + pop_access_scope (pattern); + return result; } - tree eval = satisfy_associated_constraints (ci, args); - return eval == boolean_true_node; + return boolean_true_node; +} + +static tree +constraint_satisfaction_value (tree t, tsubst_flags_t complain) +{ + subst_info info (complain, NULL_TREE); + if (DECL_P (t)) + return satisfy_declaration_constraints (t, info); + else + return satisfy_constraint_expression (t, NULL_TREE, info); } -/* Returns true if the constraints are satisfied by ARGS. - Here, T can be either a constraint or a constrained - declaration. */ +static tree +constraint_satisfaction_value (tree t, tree args, tsubst_flags_t complain) +{ + subst_info info (complain, NULL_TREE); + if (DECL_P (t)) + return satisfy_declaration_constraints (t, args, info); + else + return satisfy_constraint_expression (t, args, info); +} + +/* True iff the result of satisfying T is BOOLEAN_TRUE_NODE and false + otherwise, even in the case of errors. */ + +bool +constraints_satisfied_p (tree t) +{ + return constraint_satisfaction_value (t, tf_none) == boolean_true_node; +} + +/* True iff the result of satisfying T with ARGS is BOOLEAN_TRUE_NODE + and false otherwise, even in the case of errors. */ 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; + return constraint_satisfaction_value (t, args, tf_none) == boolean_true_node; } -namespace +/* Evaluate a concept check of the form C<ARGS>. This is only used for the + evaluation of template-ids as id-expressions. */ + +tree +evaluate_concept_check (tree check, tsubst_flags_t complain) { - -/* 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; + if (check == error_mark_node) + return error_mark_node; + + gcc_assert (concept_check_p (check)); + + /* Check for satisfaction without diagnostics. */ + subst_info quiet (tf_none, NULL_TREE); + tree result = satisfy_constraint_expression (check, NULL_TREE, quiet); + if (result == error_mark_node && (complain & tf_error)) + { + /* Replay the error with re-normalized requirements. */ + subst_info noisy (tf_warning_or_error, NULL_TREE); + satisfy_constraint_expression (check, NULL_TREE, noisy); + } + return result; } -} /* namespace */ - /*--------------------------------------------------------------------------- Semantic analysis of requires-expressions ---------------------------------------------------------------------------*/ /* Finish a requires expression for the given PARMS (possibly - null) and the non-empty sequence of requirements. */ + null) and the non-empty sequence of requirements. */ + tree -finish_requires_expr (tree parms, tree reqs) +finish_requires_expr (location_t loc, tree parms, tree reqs) { /* Modify the declared parameters by removing their context so they don't refer to the enclosing scope and explicitly @@ -2447,62 +2809,82 @@ tree r = build_min (REQUIRES_EXPR, boolean_type_node, parms, reqs); TREE_SIDE_EFFECTS (r) = false; TREE_CONSTANT (r) = true; + SET_EXPR_LOCATION (r, loc); return r; } -/* Construct a requirement for the validity of EXPR. */ +/* Construct a requirement for the validity of EXPR. */ + tree -finish_simple_requirement (tree expr) +finish_simple_requirement (location_t loc, tree expr) { - return build_nt (SIMPLE_REQ, expr); + tree r = build_nt (SIMPLE_REQ, expr); + SET_EXPR_LOCATION (r, loc); + return r; } -/* Construct a requirement for the validity of TYPE. */ +/* Construct a requirement for the validity of TYPE. */ + tree -finish_type_requirement (tree type) +finish_type_requirement (location_t loc, tree type) { - return build_nt (TYPE_REQ, type); + tree r = build_nt (TYPE_REQ, type); + SET_EXPR_LOCATION (r, loc); + return r; } /* 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. */ + NOEXCEPT_P is true iff the noexcept keyword was specified. */ + tree -finish_compound_requirement (tree expr, tree type, bool noexcept_p) +finish_compound_requirement (location_t loc, tree expr, tree type, bool noexcept_p) { tree req = build_nt (COMPOUND_REQ, expr, type); + SET_EXPR_LOCATION (req, loc); COMPOUND_REQ_NOEXCEPT_P (req) = noexcept_p; return req; } -/* Finish a nested requirement. */ +/* Finish a nested requirement. */ + tree -finish_nested_requirement (tree expr) +finish_nested_requirement (location_t loc, tree expr) { - return build_nt (NESTED_REQ, expr); + /* Save the normalized constraint and complete set of normalization + arguments with the requirement. We keep the complete set of arguments + around for re-normalization during diagnostics. */ + tree args = current_template_parms + ? template_parms_to_args (current_template_parms) : NULL_TREE; + tree norm = normalize_constraint_expression (expr, args, false); + tree info = build_tree_list (args, norm); + + /* Build the constraint, saving its normalization as its type. */ + tree r = build1 (NESTED_REQ, info, expr); + SET_EXPR_LOCATION (r, loc); + return r; } -// Check that FN satisfies the structural requirements of a -// function concept definition. +/* 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. + /* Check that the function is comprised of only a 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. + /* 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. */ + /* Check that the definition is written correctly. */ if (TREE_CODE (body) != RETURN_EXPR) { location_t loc = DECL_SOURCE_LOCATION (fn); @@ -2582,108 +2964,104 @@ 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. */ +/* Returns true when the the constraints in CI (with arguments + ARGS) strictly subsume the associated constraints of TMPL. */ bool -strictly_subsumes (tree a, tree b) +strictly_subsumes (tree ci, tree args, tree tmpl) { - return subsumes (a, b) && !subsumes (b, a); + tree n1 = get_normalized_constraints_from_info (ci, args, NULL_TREE); + tree n2 = get_normalized_constraints_from_decl (tmpl); + + return subsumes (n1, n2) && !subsumes (n2, n1); +} + +/* REturns true when the constraints in CI (with arguments ARGS) subsume + the associated constraints of TMPL. */ + +bool +weakly_subsumes (tree ci, tree args, tree tmpl) +{ + tree n1 = get_normalized_constraints_from_info (ci, args, NULL_TREE); + tree n2 = get_normalized_constraints_from_decl (tmpl); + + return subsumes (n1, n2); } /* 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. */ + Returns 1 if D1 is more constrained than D2, -1 if D2 is more constrained + than D1, and 0 otherwise. */ int more_constrained (tree d1, tree d2) { - tree c1 = get_constraints (d1); - tree c2 = get_constraints (d2); + tree n1 = get_normalized_constraints_from_decl (d1); + tree n2 = get_normalized_constraints_from_decl (d2); + int winner = 0; - if (subsumes_constraints (c1, c2)) + if (subsumes (n1, n2)) ++winner; - if (subsumes_constraints (c2, c1)) + if (subsumes (n2, n1)) --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. */ +/* Return whether D1 is at least as constrained as D2. */ bool at_least_as_constrained (tree d1, tree d2) { - tree c1 = get_constraints (d1); - tree c2 = get_constraints (d2); - return subsumes_constraints (c1, c2); + tree n1 = get_normalized_constraints_from_decl (d1); + tree n2 = get_normalized_constraints_from_decl (d2); + + return subsumes (n1, n2); } - /*--------------------------------------------------------------------------- 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 () +/* Returns the best location to diagnose a constraint error. */ + +static location_t +get_constraint_error_location (tree t) { - 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; + /* If we have a specific location give it. */ + tree expr = CONSTR_EXPR (t); + if (location_t loc = cp_expr_location (expr)) + return loc; + + /* If the constraint is normalized from a requires-clause, give + the location as that of the constrained declaration. */ + tree cxt = CONSTR_CONTEXT (t); + tree src = TREE_VALUE (cxt); + if (!src) + /* TODO: This only happens for constrained non-template declarations. */ + return input_location; + if (DECL_P (src)) + return DECL_SOURCE_LOCATION (src); + + /* Otherwise, give the location as the defining concept. */ + gcc_assert (concept_check_p (src)); + tree id = unpack_concept_check (src); + tree tmpl = TREE_OPERAND (id, 0); + if (OVL_P (tmpl)) + tmpl = OVL_FIRST (tmpl); + return DECL_SOURCE_LOCATION (tmpl); } -/* 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. */ +/* Emit a diagnostic for a failed trait. */ void -diagnose_trait_expression (location_t loc, tree, tree cur, tree args) +diagnose_trait_expr (tree expr, tree args) { - if (constraint_expression_satisfied_p (cur, args)) - return; - if (elide_constraint_failure_p()) - return; - - tree expr = PRED_CONSTR_EXPR (cur); + location_t loc = cp_expr_location (expr); + + /* Build a "fake" version of the instantiated trait, so we can + get the instantiated types from result. */ ++processing_template_decl; expr = tsubst_expr (expr, args, tf_none, NULL_TREE, false); --processing_template_decl; @@ -2693,13 +3071,13 @@ switch (TRAIT_EXPR_KIND (expr)) { case CPTK_HAS_NOTHROW_ASSIGN: - inform (loc, " %qT is not nothrow copy assignable", t1); + inform (loc, " %qT is not %<nothrow%> copy assignable", t1); break; case CPTK_HAS_NOTHROW_CONSTRUCTOR: - inform (loc, " %qT is not nothrow default constructible", t1); + inform (loc, " %qT is not %<nothrow%> default constructible", t1); break; case CPTK_HAS_NOTHROW_COPY: - inform (loc, " %qT is not nothrow copy constructible", t1); + inform (loc, " %qT is not %<nothrow%> copy constructible", t1); break; case CPTK_HAS_TRIVIAL_ASSIGN: inform (loc, " %qT is not trivially copy assignable", t1); @@ -2760,390 +3138,232 @@ } } -/* Diagnose the expression of a predicate constraint. */ - -void -diagnose_other_expression (location_t loc, tree, tree cur, tree args) +static tree +diagnose_valid_expression (tree expr, tree args, tree in_decl) { - if (constraint_expression_satisfied_p (cur, args)) - return; - if (elide_constraint_failure_p()) - return; - inform (loc, "%qE evaluated to false", cur); + tree result = tsubst_expr (expr, args, tf_none, in_decl, false); + if (result != error_mark_node) + return result; + + location_t loc = cp_expr_loc_or_input_loc (expr); + inform (loc, "the required expression %qE is invalid", expr); + + /* TODO: Replay the substitution to diagnose the error? */ + // tsubst_expr (expr, args, tf_error, in_decl, false); + + return error_mark_node; } -/* Do our best to infer meaning from predicates. */ - -inline void -diagnose_predicate_constraint (location_t loc, tree orig, tree cur, tree args) +static tree +diagnose_valid_type (tree type, tree args, tree in_decl) { - if (TREE_CODE (PRED_CONSTR_EXPR (cur)) == TRAIT_EXPR) - diagnose_trait_expression (loc, orig, cur, args); - else - diagnose_other_expression (loc, orig, cur, args); + tree result = tsubst (type, args, tf_none, in_decl); + if (result != error_mark_node) + return result; + + location_t loc = cp_expr_loc_or_input_loc (type); + inform (loc, "the required type %qT is invalid", type); + + /* TODO: Replay the substitution to diagnose the error? */ + // tsubst (type, args, tf_error, in_decl); + + return error_mark_node; } -/* Diagnose a failed pack expansion, possibly containing constraints. */ - -void -diagnose_pack_expansion (location_t loc, tree, tree cur, tree args) +static void +diagnose_simple_requirement (tree req, tree args, tree in_decl) { - if (constraint_expression_satisfied_p (cur, args)) + diagnose_valid_expression (TREE_OPERAND (req, 0), args, in_decl); +} + +static void +diagnose_compound_requirement (tree req, tree args, tree in_decl) +{ + tree expr = TREE_OPERAND (req, 0); + expr = diagnose_valid_expression (expr, args, in_decl); + if (expr == error_mark_node) return; - if (elide_constraint_failure_p()) + + location_t loc = cp_expr_loc_or_input_loc (expr); + + /* Check the noexcept condition. */ + if (COMPOUND_REQ_NOEXCEPT_P (req) && !expr_noexcept_p (expr, tf_none)) + inform (loc, "%qE is not %<noexcept%>", expr); + + tree type = TREE_OPERAND (req, 1); + type = diagnose_valid_type (type, args, in_decl); + if (type == error_mark_node) 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)) + if (type) { - 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); + subst_info quiet (tf_none, in_decl); + subst_info noisy (tf_error, in_decl); + + /* Check the expression against the result type. */ + if (tree placeholder = type_uses_auto (type)) + { + if (!type_deducible_p (expr, type, placeholder, args, quiet)) + { + tree orig_expr = TREE_OPERAND (req, 0); + inform (loc, "%qE does not satisfy return-type-requirement", + orig_expr); + + /* Further explain the reason for the error. */ + type_deducible_p (expr, type, placeholder, args, noisy); + } + } + else if (!expression_convertible_p (expr, type, quiet)) + { + tree orig_expr = TREE_OPERAND (req, 0); + inform (loc, "cannot convert %qE to %qT", orig_expr, type); + + /* Further explain the reason for the error. */ + expression_convertible_p (expr, type, noisy); + } } } -/* 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) +static void +diagnose_type_requirement (tree req, tree args, tree in_decl) { - if (constraints_satisfied_p (cur, args)) + tree type = TREE_OPERAND (req, 0); + diagnose_valid_type (type, args, in_decl); +} + +static void +diagnose_nested_requirement (tree req, tree args) +{ + /* Quietly check for satisfaction first. We can elaborate details + later if needed. */ + tree norm = TREE_VALUE (TREE_TYPE (req)); + subst_info info (tf_none, NULL_TREE); + tree result = satisfy_constraint (norm, args, info); + if (result == boolean_true_node) return; - tree 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) + tree expr = TREE_OPERAND (req, 0); + location_t loc = cp_expr_location (expr); + inform (loc, "nested requirement %qE is not satisfied", expr); + + /* TODO: Replay the substitution to diagnose the error? */ + // subst_info noisy (tf_warning_or_error, NULL_TREE); + // satisfy_constraint (norm, args, info); +} + +static void +diagnose_requirement (tree req, tree args, tree in_decl) +{ + iloc_sentinel loc_s (cp_expr_location (req)); + switch (TREE_CODE (req)) { - 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; + case SIMPLE_REQ: + return diagnose_simple_requirement (req, args, in_decl); + case COMPOUND_REQ: + return diagnose_compound_requirement (req, args, in_decl); + case TYPE_REQ: + return diagnose_type_requirement (req, args, in_decl); + case NESTED_REQ: + return diagnose_nested_requirement (req, args); + default: + gcc_unreachable (); } - - tree sub = build_tree_list (tmpl, targs); - /* Update to the expanded definitions. */ - cur = expand_concept (decl, targs); - if (cur == error_mark_node) +} + +static void +diagnose_requires_expr (tree expr, tree args, tree in_decl) +{ + local_specialization_stack stack (lss_copy); + tree parms = TREE_OPERAND (expr, 0); + tree body = TREE_OPERAND (expr, 1); + + cp_unevaluated u; + subst_info info (tf_warning_or_error, NULL_TREE); + tree vars = tsubst_constraint_variables (parms, args, info); + if (vars == error_mark_node) + return; + + tree p = body; + while (p) { - 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); + tree req = TREE_VALUE (p); + diagnose_requirement (req, args, in_decl); + p = TREE_CHAIN (p); + } +} + +/* Diagnose a substitution failure in the atomic constraint T. Note that + ARGS have been previously instantiated through the parameter map. */ + +static void +diagnose_atomic_constraint (tree t, tree args, subst_info info) +{ + /* If the constraint is already ill-formed, we've previously diagnosed + the reason. We should still say why the constraints aren't satisfied. */ + if (t == error_mark_node) + { + location_t loc; + if (info.in_decl) + loc = DECL_SOURCE_LOCATION (info.in_decl); + else + loc = input_location; + inform (loc, "invalid constraints"); 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)) + location_t loc = get_constraint_error_location (t); + iloc_sentinel loc_s (loc); + + /* Generate better diagnostics for certain kinds of expressions. */ + tree expr = ATOMIC_CONSTR_EXPR (t); + STRIP_ANY_LOCATION_WRAPPER (expr); + switch (TREE_CODE (expr)) { - 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); + case TRAIT_EXPR: + diagnose_trait_expr (expr, args); + break; + case REQUIRES_EXPR: + diagnose_requires_expr (expr, args, info.in_decl); 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); + case INTEGER_CST: + /* This must be either 0 or false. */ + inform (loc, "%qE is never satisfied", expr); 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; + inform (loc, "the expression %qE evaluated to %<false%>", expr); } } -/* 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) +GTY(()) tree current_failed_constraint; + +diagnosing_failed_constraint:: +diagnosing_failed_constraint (tree t, tree args, bool diag) + : diagnosing_error (diag) { - 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); + if (diagnosing_error) + current_failed_constraint = tree_cons (args, t, current_failed_constraint); } -} // namespace - -/* Emit diagnostics detailing the failure ARGS to satisfy the - constraints of T. Here, T can be either a constraint - or a declaration. */ +diagnosing_failed_constraint:: +~diagnosing_failed_constraint () +{ + if (diagnosing_error && current_failed_constraint) + current_failed_constraint = TREE_CHAIN (current_failed_constraint); +} + +/* 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); + inform (loc, "constraints not satisfied"); + + /* Replay satisfaction, but diagnose errors. */ + if (!args) + constraint_satisfaction_value (t, tf_warning_or_error); 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); + constraint_satisfaction_value (t, args, tf_warning_or_error); } + +#include "gt-cp-constraint.h"