diff options
Diffstat (limited to 'gcc/cp/constraint.cc')
-rw-r--r-- | gcc/cp/constraint.cc | 1535 |
1 files changed, 991 insertions, 544 deletions
diff --git a/gcc/cp/constraint.cc b/gcc/cp/constraint.cc index af7a593..311d025 100644 --- a/gcc/cp/constraint.cc +++ b/gcc/cp/constraint.cc @@ -22,6 +22,7 @@ along with GCC; see the file COPYING3. If not see #include "system.h" #include "coretypes.h" #include "tm.h" +#include "timevar.h" #include "hash-set.h" #include "machmode.h" #include "vec.h" @@ -55,7 +56,9 @@ along with GCC; see the file COPYING3. If not see static inline bool constraint_p (tree_code c) { - return (PRED_CONSTR <= c && c <= DISJ_CONSTR) || c == ERROR_MARK; + return ((PRED_CONSTR <= c && c <= DISJ_CONSTR) + || c == EXPR_PACK_EXPANSION + || c == ERROR_MARK); } /* Returns true if T is a constraint. Note that error_mark_node @@ -67,14 +70,6 @@ constraint_p (tree t) return constraint_p (TREE_CODE (t)); } -/* Make a predicate constraint from the given expression. */ - -tree -make_predicate_constraint (tree expr) -{ - return build_nt (PRED_CONSTR, expr); -} - /* 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, @@ -132,6 +127,53 @@ function_concept_check_p (tree t) return false; } +/* Returns true if any of the arguments in the template + argument list is a wildcard or wildcard pack. */ + +bool +contains_wildcard_p (tree args) +{ + for (int i = 0; i < TREE_VEC_LENGTH (args); ++i) + { + tree arg = TREE_VEC_ELT (args, i); + if (TREE_CODE (arg) == WILDCARD_DECL) + return true; + } + return false; +} + +/* Build a new call expression, but don't actually generate a + new function call. We just want the tree, not the semantics. */ + +inline tree +build_call_check (tree id) +{ + ++processing_template_decl; + vec<tree, va_gc> *fargs = make_tree_vector(); + tree call = finish_call_expr (id, &fargs, false, false, tf_none); + release_tree_vector (fargs); + --processing_template_decl; + return call; +} + +/* Build an expression that will check a variable concept. If any + argument contains a wildcard, don't try to finish the variable + template because we can't substitute into a non-existent + declaration. */ + +tree +build_variable_check (tree id) +{ + gcc_assert (TREE_CODE (id) == TEMPLATE_ID_EXPR); + if (contains_wildcard_p (TREE_OPERAND (id, 1))) + return id; + + ++processing_template_decl; + tree var = finish_template_variable (id); + --processing_template_decl; + return var; +} + /*--------------------------------------------------------------------------- Resolution of qualified concept names ---------------------------------------------------------------------------*/ @@ -160,6 +202,7 @@ function_concept_check_p (tree t) static tree resolve_constraint_check (tree ovl, tree args) { + int nerrs = 0; tree cands = NULL_TREE; for (tree p = ovl; p != NULL_TREE; p = OVL_NEXT (p)) { @@ -185,15 +228,21 @@ resolve_constraint_check (tree ovl, tree args) ++processing_template_decl; tree parms = TREE_VALUE (DECL_TEMPLATE_PARMS (tmpl)); if (tree subst = coerce_template_parms (parms, args, tmpl)) - if (subst != error_mark_node) - cands = tree_cons (subst, fn, cands); + { + if (subst == error_mark_node) + ++nerrs; + else + cands = tree_cons (subst, fn, cands); + } --processing_template_decl; } - // If we didn't find a unique candidate, then this is - // not a constraint check. - if (!cands || TREE_CHAIN (cands)) - return NULL_TREE; + if (!cands) + /* We either had no candidates or failed deductions. */ + return nerrs ? error_mark_node : NULL_TREE; + else if (TREE_CHAIN (cands)) + /* There are multiple candidates. */ + return error_mark_node; return cands; } @@ -250,14 +299,16 @@ resolve_variable_concept_check (tree id) assuming that it works. Note that failing to deduce will result in diagnostics. */ tree parms = INNERMOST_TEMPLATE_PARMS (DECL_TEMPLATE_PARMS (tmpl)); + ++processing_template_decl; tree result = coerce_template_parms (parms, args, tmpl); + --processing_template_decl; if (result != error_mark_node) { tree decl = DECL_TEMPLATE_RESULT (tmpl); return build_tree_list (result, decl); } else - return NULL_TREE; + return error_mark_node; } @@ -315,45 +366,119 @@ deduce_concept_introduction (tree expr) namespace { /*--------------------------------------------------------------------------- - Lifting of concept definitions + Constraint implication learning ---------------------------------------------------------------------------*/ -/* Part of constraint normalization. Whenever we find a reference to - a variable concept or a call to a function concept, we lift or - inline that concept's definition into the constraint. This ensures - that constraints are always checked in the immediate instantiation - context. */ +/* 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: -tree lift_expression (tree); + template<typename T> concept bool C = ...; + template<typenaem T> concept bool D = C<T> && true; -/* If the tree T has operands, then lift any concepts out of them. */ -tree -lift_operands (tree t) + 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) { - if (int n = tree_operand_length (t)) + switch (cxt) { - t = copy_node (t); - for (int i = 0; i < n; ++i) - TREE_OPERAND (t, i) = lift_expression (TREE_OPERAND (t, i)); + 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; } - return t; } -/* Recursively lift all operands of the function call. Also, check - that the call target is not accidentally a variable concept - since that's ill-formed. */ -tree -lift_function_call (tree t) +void +learn_logical_operation (tree parent, tree constr, implication_context cxt) { - gcc_assert (TREE_CODE (t) == CALL_EXPR); - gcc_assert (!VAR_P (CALL_EXPR_FN (t))); - return lift_operands (t); + learn_implications (parent, TREE_OPERAND (constr, 0), cxt); + learn_implications (parent, TREE_OPERAND (constr, 1), cxt); } -/* Inline a function (concept) definition by substituting - ARGS into its body. */ +void +learn_implications (tree parent, tree constr, implication_context cxt) +{ + switch (TREE_CODE (constr)) + { + case CHECK_CONSTR: + return learn_implication (parent, constr, cxt); + + case CONJ_CONSTR: + if (cxt == disjunction_cxt) + return; + return learn_logical_operation (parent, constr, cxt); + + case DISJ_CONSTR: + if (cxt == conjunction_cxt) + return; + return learn_logical_operation (parent, constr, cxt); + + default: + break; + } +} + +/* Quickly scan the top-level constraints of CONSTR to learn and + cache logical relations between concepts. The search does not + include conjunctions of disjunctions or vice versa. */ + +void +learn_implications (tree tmpl, tree args, tree constr) +{ + /* Don't memoize relations between non-dependent arguemnts. It's not + helpful. */ + if (!uses_template_parms (args)) + return; + + /* Build a check constraint for the purpose of caching. */ + tree parent = build_nt (CHECK_CONSTR, tmpl, args); + + /* Start learning based on the kind of the top-level contraint. */ + if (TREE_CODE (constr) == CONJ_CONSTR) + return learn_logical_operation (parent, constr, conjunction_cxt); + else if (TREE_CODE (constr) == DISJ_CONSTR) + return learn_logical_operation (parent, constr, disjunction_cxt); + else if (TREE_CODE (constr) == CHECK_CONSTR) + /* This is the rare concept alias case. */ + return learn_implication (parent, constr, equivalence_cxt); +} + +/*--------------------------------------------------------------------------- + Expansion of concept definitions +---------------------------------------------------------------------------*/ + +/* Returns the expression of a function concept. */ + tree -lift_function_definition (tree fn, tree args) +get_returned_expression (tree fn) { /* Extract the body of the function minus the return expression. */ tree body = DECL_SAVED_TREE (fn); @@ -364,202 +489,107 @@ lift_function_definition (tree fn, tree args) if (TREE_CODE (body) != RETURN_EXPR) return error_mark_node; - body = TREE_OPERAND (body, 0); - - /* Substitute template arguments to produce our inline expression. */ - tree result = tsubst_expr (body, args, tf_none, NULL_TREE, false); - if (result == error_mark_node) - return error_mark_node; - - return lift_expression (result); + return TREE_OPERAND (body, 0); } -/* Inline a reference to a function concept. */ -tree -lift_call_expression (tree t) -{ - /* Try to resolve this function call as a concept. If not, then - it can be returned as-is. */ - tree check = resolve_constraint_check (t); - if (!check) - return lift_function_call (t); - if (check == error_mark_node) - return error_mark_node; - - tree fn = TREE_VALUE (check); - tree args = TREE_PURPOSE (check); - return lift_function_definition (fn, args); -} +/* Returns the initializer of a variable concept. */ tree -lift_variable_initializer (tree var, tree args) +get_variable_initializer (tree var) { - /* Extract the body from the variable initializer. */ tree init = DECL_INITIAL (var); if (!init) return error_mark_node; + return init; +} - /* Substitute the arguments to form our new inline expression. */ - tree result = tsubst_expr (init, args, tf_none, NULL_TREE, false); - if (result == error_mark_node) - return error_mark_node; +/* Returns the definition of a variable or function concept. */ - return lift_expression (result); +tree +get_concept_definition (tree decl) +{ + if (TREE_CODE (decl) == VAR_DECL) + return get_variable_initializer (decl); + else if (TREE_CODE (decl) == FUNCTION_DECL) + return get_returned_expression (decl); + gcc_unreachable (); } -/* Determine if a template-id is a variable concept and inline. */ +int expansion_level = 0; -tree -lift_template_id (tree t) +struct expanding_concept_sentinel { - if (tree info = resolve_variable_concept_check (t)) - { - tree decl = TREE_VALUE (info); - tree args = TREE_PURPOSE (info); - return lift_variable_initializer (decl, args); - } + expanding_concept_sentinel () + { + ++expansion_level; + } - /* Check that we didn't refer to a function concept like - a variable. + ~expanding_concept_sentinel() + { + --expansion_level; + } +}; - TODO: Add a note on how to fix this. */ - tree tmpl = TREE_OPERAND (t, 0); - if (TREE_CODE (tmpl) == OVERLOAD) - { - tree fn = OVL_FUNCTION (tmpl); - if (TREE_CODE (fn) == TEMPLATE_DECL - && DECL_DECLARED_CONCEPT_P (DECL_TEMPLATE_RESULT (fn))) - { - error_at (location_of (t), - "invalid reference to function concept %qD", fn); - return error_mark_node; - } - } - return t; -} +} /* namespace */ -/* Lift any constraints appearing in a nested requirement of - a requires-expression. */ -tree -lift_requires_expression (tree t) +/* Returns true when a concept is being expanded. */ + +bool +expanding_concept() { - tree parms = TREE_OPERAND (t, 0); - tree reqs = TREE_OPERAND (t, 1); - tree result = NULL_TREE; - for (; reqs != NULL_TREE; reqs = TREE_CHAIN (reqs)) - { - tree req = TREE_VALUE (reqs); - if (TREE_CODE (req) == NESTED_REQ) - { - tree expr = lift_expression (TREE_OPERAND (req, 0)); - req = finish_nested_requirement (expr); - } - result = tree_cons (NULL_TREE, req, result); - } - return finish_requires_expr (parms, result); + return expansion_level > 0; } -/* Inline references to specializations of concepts. */ +/* Expand a concept declaration (not a template) and its arguments to + a constraint defined by the concept's initializer or definition. */ + tree -lift_expression (tree t) +expand_concept (tree decl, tree args) { - if (t == NULL_TREE) - return NULL_TREE; + expanding_concept_sentinel sentinel; - if (t == error_mark_node) - return error_mark_node; + if (TREE_CODE (decl) == TEMPLATE_DECL) + decl = DECL_TEMPLATE_RESULT (decl); + tree tmpl = DECL_TI_TEMPLATE (decl); - /* Concepts can be referred to by call or variable. All other - nodes are preserved. */ - switch (TREE_CODE (t)) - { - case CALL_EXPR: - return lift_call_expression (t); + /* Check for a previous specialization. */ + if (tree spec = get_concept_expansion (tmpl, args)) + return spec; - case TEMPLATE_ID_EXPR: - return lift_template_id (t); + /* Substitute the arguments to form a new definition expression. */ + tree def = get_concept_definition (decl); - case REQUIRES_EXPR: - return lift_requires_expression (t); - - case EXPR_PACK_EXPANSION: - /* Use copy_node rather than make_pack_expansion so that - PACK_EXPANSION_PARAMETER_PACKS stays the same. */ - t = copy_node (t); - SET_PACK_EXPANSION_PATTERN - (t, lift_expression (PACK_EXPANSION_PATTERN (t))); - return t; - - case TREE_LIST: - { - t = copy_node (t); - TREE_VALUE (t) = lift_expression (TREE_VALUE (t)); - TREE_CHAIN (t) = lift_expression (TREE_CHAIN (t)); - return t; - } + ++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; - default: - return lift_operands (t); - } + /* 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); } -/*--------------------------------------------------------------------------- - Transformation of expressions into constraints ----------------------------------------------------------------------------*/ - -/* Part of constraint normalization. The following functions rewrite - expressions as constraints. */ - -tree transform_expression (tree); - -/* Check that the logical-or or logical-and expression does - not result in a call to a user-defined user-defined operator - (temp.constr.op). Returns true if the logical operator is - admissible and false otherwise. */ -bool -check_logical_expr (tree t) -{ - /* We can't do much for type dependent expressions. */ - if (type_dependent_expression_p (t)) - return true; +/*--------------------------------------------------------------------------- + Stepwise normalization of expressions - /* Resolve the logical operator. Note that template processing is - disabled so we get the actual call or target expression back. - not_processing_template_sentinel sentinel. - - TODO: This check is actually subsumed by the requirement that - constraint operands have type bool. I'm not sure we need it - unless we allow conversions. */ - tree arg1 = TREE_OPERAND (t, 0); - tree arg2 = TREE_OPERAND (t, 1); - tree ovl = NULL_TREE; - tree expr = build_x_binary_op (EXPR_LOC_OR_LOC (arg2, input_location), - TREE_CODE (t), - arg1, TREE_CODE (arg1), - arg2, TREE_CODE (arg2), - &ovl, - tf_none); - if (TREE_CODE (expr) != TREE_CODE (t)) - { - error ("user-defined operator %qs in constraint %q+E", - operator_name_info[TREE_CODE (t)].name, t); - return false; - } - return true; -} +This set of functions will transform an expression into a constraint +in a sequence of steps. Normalization does not not look into concept +definitions. +---------------------------------------------------------------------------*/ /* Transform a logical-or or logical-and expression into either a conjunction or disjunction. */ tree -xform_logical (tree t, tree_code c) +normalize_logical_operation (tree t, tree_code c) { - if (!check_logical_expr (t)) - return error_mark_node; - tree t0 = transform_expression (TREE_OPERAND (t, 0)); - tree t1 = transform_expression (TREE_OPERAND (t, 1)); + tree t0 = normalize_expression (TREE_OPERAND (t, 0)); + tree t1 = normalize_expression (TREE_OPERAND (t, 1)); return build_nt (c, t0, t1); } @@ -567,7 +597,7 @@ xform_logical (tree t, tree_code c) for its expression. */ inline tree -xform_simple_requirement (tree t) +normalize_simple_requirement (tree t) { return build_nt (EXPR_CONSTR, TREE_OPERAND (t, 0)); } @@ -575,7 +605,7 @@ xform_simple_requirement (tree t) /* A type requirement T introduce a type constraint for its type. */ inline tree -xform_type_requirement (tree t) +normalize_type_requirement (tree t) { return build_nt (TYPE_CONSTR, TREE_OPERAND (t, 0)); } @@ -589,7 +619,7 @@ xform_type_requirement (tree t) includes an exception constraint. */ tree -xform_compound_requirement (tree t) +normalize_compound_requirement (tree t) { tree expr = TREE_OPERAND (t, 0); tree constr = build_nt (EXPR_CONSTR, TREE_OPERAND (t, 0)); @@ -627,29 +657,29 @@ xform_compound_requirement (tree t) will guarantee that the constraint is never satisfied. */ inline tree -xform_nested_requirement (tree t) +normalize_nested_requirement (tree t) { - return transform_expression (TREE_OPERAND (t, 0)); + return normalize_expression (TREE_OPERAND (t, 0)); } /* Transform a requirement T into one or more constraints. */ tree -xform_requirement (tree t) +normalize_requirement (tree t) { switch (TREE_CODE (t)) { case SIMPLE_REQ: - return xform_simple_requirement (t); + return normalize_simple_requirement (t); case TYPE_REQ: - return xform_type_requirement (t); + return normalize_type_requirement (t); case COMPOUND_REQ: - return xform_compound_requirement (t); + return normalize_compound_requirement (t); case NESTED_REQ: - return xform_nested_requirement (t); + return normalize_nested_requirement (t); default: gcc_unreachable (); @@ -661,46 +691,165 @@ xform_requirement (tree t) constraints. */ tree -xform_requirements (tree t) +normalize_requirements (tree t) { tree result = NULL_TREE; for (; t; t = TREE_CHAIN (t)) { - tree constr = xform_requirement (TREE_VALUE (t)); + tree constr = normalize_requirement (TREE_VALUE (t)); result = conjoin_constraints (result, constr); } return result; } -/* Transform a requires-expression into a parameterized constraint. */ +/* 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 -xform_requires_expr (tree t) +normalize_requires_expression (tree t) { - tree operand = xform_requirements (TREE_OPERAND (t, 1)); + 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; } -/* Transform an expression into an atomic predicate constraint. - After substitution, the expression of a predicate constraint - shall have type bool (temp.constr.pred). For non-type-dependent - expressions, we can check that now. */ +/* For a template-id referring to a variable concept, returns + a check constraint. Otherwise, returns a predicate constraint. */ tree -xform_atomic (tree t) +normalize_template_id_expression (tree t) { - if (TREE_TYPE (t) && !type_dependent_expression_p (t)) - { - tree type = cv_unqualified (TREE_TYPE (t)); - if (!same_type_p (type, boolean_type_node)) - { - error ("predicate constraint %q+E does not have type %<bool%>", 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 tmpl = TREE_OPERAND (t, 0); + if (TREE_CODE (tmpl) == OVERLOAD) + { + tree fn = OVL_FUNCTION (tmpl); + if (TREE_CODE (fn) == TEMPLATE_DECL + && DECL_DECLARED_CONCEPT_P (DECL_TEMPLATE_RESULT (fn))) + { + error_at (location_of (t), + "invalid reference to function concept %qD", fn); + return error_mark_node; + } + } + + return build_nt (PRED_CONSTR, t); +} + +/* For a call expression to a function concept, returns a check + constraint. Otherwise, returns a predicate constraint. */ + +tree +normalize_call_expression (tree t) +{ + /* Try to resolve this function call as a concept. If not, then + it can be returned as a predicate constraint. */ + tree check = resolve_constraint_check (t); + if (!check) + return build_nt (PRED_CONSTR, t); + if (check == error_mark_node) + { + /* TODO: Improve diagnostics. We could report why the reference + is invalid. */ + error ("invalid reference to concept %qE", t); + return error_mark_node; + } + + tree fn = TREE_VALUE (check); + tree args = TREE_PURPOSE (check); + return build_nt (CHECK_CONSTR, fn, args); +} + +/* If T is a call to an overloaded && or || operator, diagnose that + as a non-SFINAEable error. Returns true if an error is emitted. + + TODO: It would be better to diagnose this at the point of definition, + if possible. Perhaps we should immediately do a first-pass normalization + of a concept definition to catch obvious non-dependent errors like + this. */ + +bool +check_for_logical_overloads (tree t) +{ + if (TREE_CODE (t) != CALL_EXPR) + return false; + + tree fn = CALL_EXPR_FN (t); + + /* For member calls, try extracting the function from the + component ref. */ + if (TREE_CODE (fn) == COMPONENT_REF) + { + fn = TREE_OPERAND (fn, 1); + if (TREE_CODE (fn) == BASELINK) + fn = BASELINK_FUNCTIONS (fn); + } + + if (TREE_CODE (fn) != FUNCTION_DECL) + return false; + + if (DECL_OVERLOADED_OPERATOR_P (fn)) + { + location_t loc = EXPR_LOC_OR_LOC (t, input_location); + error_at (loc, "constraint %qE, uses overloaded operator", t); + return true; + } + + return false; +} + +/* The normal form of an atom depends on the expression. The normal + form of a function call to a function concept is a check constraint + for that concept. The normal form of a reference to a variable + concept is a check constraint for that concept. Otherwise, the + constraint is a predicate constraint. */ + +tree +normalize_atom (tree t) +{ + /* We can get constraints pushed down through pack expansions, so + just return them. */ + if (constraint_p (t)) + return t; + + tree type = TREE_TYPE (t); + if (!type || type_unknown_p (t) || TREE_CODE (type) == TEMPLATE_TYPE_PARM) + ; + else if (!dependent_type_p (type)) + { + if (check_for_logical_overloads (t)) return error_mark_node; - } - } + + type = cv_unqualified (type); + if (!same_type_p (type, boolean_type_node)) + { + error ("predicate constraint %q+E does not have type %<bool%>", t); + return error_mark_node; + } + } + + if (TREE_CODE (t) == TEMPLATE_ID_EXPR) + return normalize_template_id_expression (t); + if (TREE_CODE (t) == CALL_EXPR) + return normalize_call_expression (t); return build_nt (PRED_CONSTR, t); } @@ -735,49 +884,48 @@ push_down_pack_expansion (tree exp, tree pat) leaves of the constraint so that partial ordering will work. */ tree -xform_pack_expansion (tree t) +normalize_pack_expansion (tree t) { - tree pat = transform_expression (PACK_EXPANSION_PATTERN (t)); + tree pat = normalize_expression (PACK_EXPANSION_PATTERN (t)); return push_down_pack_expansion (t, pat); } /* Transform an expression into a constraint. */ tree -xform_expr (tree t) +normalize_any_expression (tree t) { switch (TREE_CODE (t)) { case TRUTH_ANDIF_EXPR: - return xform_logical (t, CONJ_CONSTR); + return normalize_logical_operation (t, CONJ_CONSTR); case TRUTH_ORIF_EXPR: - return xform_logical (t, DISJ_CONSTR); + return normalize_logical_operation (t, DISJ_CONSTR); case REQUIRES_EXPR: - return xform_requires_expr (t); + return normalize_requires_expression (t); case BIND_EXPR: - return transform_expression (BIND_EXPR_BODY (t)); + return normalize_expression (BIND_EXPR_BODY (t)); case EXPR_PACK_EXPANSION: - return xform_pack_expansion (t); + return normalize_pack_expansion (t); default: /* All other constraints are atomic. */ - return xform_atomic (t); + return normalize_atom (t); } } /* Transform a statement into an expression. */ - tree -xform_stmt (tree t) +normalize_any_statement (tree t) { switch (TREE_CODE (t)) { case RETURN_EXPR: - return transform_expression (TREE_OPERAND (t, 0)); + return normalize_expression (TREE_OPERAND (t, 0)); default: gcc_unreachable (); } @@ -787,24 +935,22 @@ xform_stmt (tree t) /* Reduction rules for the declaration T. */ tree -xform_decl (tree t) +normalize_any_declaration (tree t) { switch (TREE_CODE (t)) { case VAR_DECL: - return xform_atomic (t); + return normalize_atom (t); default: gcc_unreachable (); } return error_mark_node; } -/* Transform a lifted expression into a constraint. This either - returns a constraint, or it returns error_mark_node when - a constraint cannot be formed. */ +/* Returns the normal form of a constraint expression. */ tree -transform_expression (tree t) +normalize_expression (tree t) { if (!t) return NULL_TREE; @@ -818,20 +964,20 @@ transform_expression (tree t) case tcc_binary: case tcc_expression: case tcc_vl_exp: - return xform_expr (t); + return normalize_any_expression (t); case tcc_statement: - return xform_stmt (t); + return normalize_any_statement (t); case tcc_declaration: - return xform_decl (t); + return normalize_any_declaration (t); case tcc_exceptional: case tcc_constant: case tcc_reference: case tcc_comparison: /* These are all atomic predicate constraints. */ - return xform_atomic (t); + return normalize_atom (t); default: /* Unhandled node kind. */ @@ -840,6 +986,7 @@ transform_expression (tree t) return error_mark_node; } + /*--------------------------------------------------------------------------- Constraint normalization ---------------------------------------------------------------------------*/ @@ -879,8 +1026,7 @@ normalize_predicate_constraint (tree t) { ++processing_template_decl; tree expr = PRED_CONSTR_EXPR (t); - tree lifted = lift_expression (expr); - tree constr = transform_expression (lifted); + tree constr = normalize_expression (expr); --processing_template_decl; return constr; } @@ -938,7 +1084,6 @@ normalize_constraint (tree t) return error_mark_node; } -} /* namespace */ // -------------------------------------------------------------------------- // @@ -1028,61 +1173,11 @@ build_constraints (tree tmpl_reqs, tree decl_reqs) ci->declarator_reqs = decl_reqs; ci->associated_constr = conjoin_constraints (tmpl_reqs, decl_reqs); - ++processing_template_decl; - ci->normalized_constr = normalize_constraint (ci->associated_constr); - --processing_template_decl; - - ci->assumptions = decompose_assumptions (ci->normalized_constr); return (tree)ci; } namespace { -/* Returns true if any of the arguments in the template - argument list is a wildcard or wildcard pack. */ -bool -contains_wildcard_p (tree args) -{ - for (int i = 0; i < TREE_VEC_LENGTH (args); ++i) - { - tree arg = TREE_VEC_ELT (args, i); - if (TREE_CODE (arg) == WILDCARD_DECL) - return true; - } - return false; -} - -/* Build a new call expression, but don't actually generate - a new function call. We just want the tree, not the - semantics. */ -inline tree -build_call_check (tree id) -{ - ++processing_template_decl; - vec<tree, va_gc> *fargs = make_tree_vector(); - tree call = finish_call_expr (id, &fargs, false, false, tf_none); - release_tree_vector (fargs); - --processing_template_decl; - return call; -} - -/* Build an expression that will check a variable concept. If any - argument contains a wildcard, don't try to finish the variable - template because we can't substitute into a non-existent - declaration. */ -tree -build_variable_check (tree id) -{ - gcc_assert (TREE_CODE (id) == TEMPLATE_ID_EXPR); - if (contains_wildcard_p (TREE_OPERAND (id, 1))) - return id; - - ++processing_template_decl; - tree var = finish_template_variable (id); - --processing_template_decl; - return var; -} - /* Construct a sequence of template arguments by prepending ARG to REST. Either ARG or REST may be null. */ tree @@ -1158,7 +1253,9 @@ build_constrained_parameter (tree cnc, tree proto, tree args) Note that the constraints are neither reduced nor decomposed. That is done only after the requires clause has been parsed - (or not). */ + (or not). + + This will always return a CHECK_CONSTR. */ tree finish_shorthand_constraint (tree decl, tree constr) { @@ -1207,7 +1304,7 @@ finish_shorthand_constraint (tree decl, tree constr) TREE_TYPE (check) = boolean_type_node; } - return make_predicate_constraint (check); + return normalize_expression (check); } /* Returns a conjunction of shorthand requirements for the template @@ -1346,7 +1443,7 @@ finish_template_introduction (tree tmpl_decl, tree intro_list) /* Associate the constraint. */ tree check = build_concept_check (tmpl_decl, NULL_TREE, check_args); - tree constr = make_predicate_constraint (check); + tree constr = normalize_expression (check); TEMPLATE_PARMS_CONSTRAINTS (current_template_parms) = constr; return parm_list; @@ -1362,41 +1459,28 @@ placeholder_extract_concept_and_args (tree t, tree &tmpl, tree &args) { if (TREE_CODE (t) == TYPE_DECL) { - /* A constrained parameter. */ - tmpl = DECL_TI_TEMPLATE (CONSTRAINED_PARM_CONCEPT (t)); - args = CONSTRAINED_PARM_EXTRA_ARGS (t); + /* A constrained parameter. Build a constraint check + based on the prototype parameter and then extract the + arguments from that. */ + tree proto = CONSTRAINED_PARM_PROTOTYPE (t); + tree check = finish_shorthand_constraint (proto, t); + placeholder_extract_concept_and_args (check, tmpl, args); return; } - gcc_assert (TREE_CODE (t) == PRED_CONSTR); - t = PRED_CONSTR_EXPR (t); - gcc_assert (TREE_CODE (t) == CALL_EXPR - || TREE_CODE (t) == TEMPLATE_ID_EXPR - || VAR_P (t)); - - if (TREE_CODE (t) == CALL_EXPR) - t = CALL_EXPR_FN (t); - if (TREE_CODE (t) == TEMPLATE_ID_EXPR) + if (TREE_CODE (t) == CHECK_CONSTR) { - tmpl = TREE_OPERAND (t, 0); - if (TREE_CODE (tmpl) == OVERLOAD) - { - gcc_assert (OVL_CHAIN (tmpl) == NULL_TREE); - tmpl = OVL_FUNCTION (tmpl); - } - args = TREE_OPERAND (t, 1); - } - else if (DECL_P (t)) - { - tmpl = DECL_TI_TEMPLATE (t); - args = DECL_TI_ARGS (t); + tree decl = CHECK_CONSTR_CONCEPT (t); + tmpl = DECL_TI_TEMPLATE (decl); + args = CHECK_CONSTR_ARGS (t); + return; } - else + gcc_unreachable (); } /* Returns true iff the placeholders C1 and C2 are equivalent. C1 - and C2 can be either PRED_CONSTR_EXPR or TEMPLATE_TYPE_PARM. */ + and C2 can be either CHECK_CONSTR or TEMPLATE_TYPE_PARM. */ bool equivalent_placeholder_constraints (tree c1, tree c2) @@ -1411,6 +1495,11 @@ equivalent_placeholder_constraints (tree c1, tree c2) return true; if (!c1 || !c2) return false; + if (c1 == error_mark_node || c2 == error_mark_node) + /* We get here during satisfaction; when a deduction constraint + fails, substitution can produce an error_mark_node for the + placeholder constraints. */ + return false; tree t1, t2, a1, a2; placeholder_extract_concept_and_args (c1, t1, a1); @@ -1419,21 +1508,20 @@ equivalent_placeholder_constraints (tree c1, tree c2) if (t1 != t2) return false; - /* Skip the first argument to avoid infinite recursion on the - placeholder auto itself. */ - bool skip1 = (TREE_CODE (c1) == PRED_CONSTR); - bool skip2 = (TREE_CODE (c2) == PRED_CONSTR); - - int len1 = (a1 ? TREE_VEC_LENGTH (a1) : 0) - skip1; - int len2 = (a2 ? TREE_VEC_LENGTH (a2) : 0) - skip2; - + int len1 = TREE_VEC_LENGTH (a1); + int len2 = TREE_VEC_LENGTH (a2); if (len1 != len2) return false; - for (int i = 0; i < len1; ++i) - if (!cp_tree_equal (TREE_VEC_ELT (a1, i + skip1), - TREE_VEC_ELT (a2, i + skip2))) + /* Skip the first argument so we don't infinitely recurse. + Also, they may differ in template parameter index. */ + for (int i = 1; i < len1; ++i) + { + tree t1 = TREE_VEC_ELT (a1, i); + tree t2 = TREE_VEC_ELT (a2, i); + if (!template_args_equal (t1, t2)) return false; + } return true; } @@ -1492,40 +1580,139 @@ tsubst_predicate_constraint (tree t, tree args, return build_nt (PRED_CONSTR, result); } +/* Substitute into a check constraint. */ + +tree +tsubst_check_constraint (tree t, tree args, + tsubst_flags_t complain, tree in_decl) +{ + tree decl = CHECK_CONSTR_CONCEPT (t); + tree tmpl = DECL_TI_TEMPLATE (decl); + tree targs = CHECK_CONSTR_ARGS (t); + + /* Substitute through by building an template-id expression + and then substituting into that. */ + tree expr = build_nt(TEMPLATE_ID_EXPR, tmpl, targs); + ++processing_template_decl; + tree result = tsubst_expr (expr, args, complain, in_decl, false); + --processing_template_decl; + + if (result == error_mark_node) + return error_mark_node; + + /* Extract the results and rebuild the check constraint. */ + decl = DECL_TEMPLATE_RESULT (TREE_OPERAND (result, 0)); + args = TREE_OPERAND (result, 1); + + return build_nt (CHECK_CONSTR, decl, args); +} + /* Substitute into the conjunction of constraints. Returns error_mark_node if substitution into either operand fails. */ + tree -tsubst_conjunction (tree t, tree args, - tsubst_flags_t complain, tree in_decl) +tsubst_logical_operator (tree t, tree args, + tsubst_flags_t complain, tree in_decl) { tree t0 = TREE_OPERAND (t, 0); tree r0 = tsubst_constraint (t0, args, complain, in_decl); + if (r0 == error_mark_node) + return error_mark_node; tree t1 = TREE_OPERAND (t, 1); tree r1 = tsubst_constraint (t1, args, complain, in_decl); - return build_nt (CONJ_CONSTR, r0, r1); + if (r1 == error_mark_node) + return error_mark_node; + return build_nt (TREE_CODE (t), r0, r1); } -/* Substitute ARGS into the constraint T. */ +namespace { + +/* Substitute ARGS into the expression constraint T. */ + tree -tsubst_constraint (tree t, tree args, tsubst_flags_t complain, tree in_decl) +tsubst_expr_constr (tree t, tree args, tsubst_flags_t complain, tree in_decl) { - if (t == NULL_TREE) - return t; - if (TREE_CODE (t) == CONJ_CONSTR) - return tsubst_conjunction (t, args, complain, in_decl); - else if (TREE_CODE (t) == PRED_CONSTR) - return tsubst_predicate_constraint (t, args, complain, in_decl); - else - gcc_unreachable (); - return error_mark_node; + 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); } -namespace { +/* Substitute ARGS into the type constraint T. */ + +tree +tsubst_type_constr (tree t, tree args, tsubst_flags_t complain, tree in_decl) +{ + tree type = TYPE_CONSTR_TYPE (t); + tree ret = tsubst (type, args, complain, in_decl); + if (ret == error_mark_node) + return error_mark_node; + return build_nt (TYPE_CONSTR, ret); +} + +/* Substitute ARGS into the implicit conversion constraint T. */ + +tree +tsubst_implicit_conversion_constr (tree t, tree args, tsubst_flags_t complain, + tree in_decl) +{ + cp_unevaluated guard; + tree expr = ICONV_CONSTR_EXPR (t); + tree type = ICONV_CONSTR_TYPE (t); + tree new_expr = tsubst_expr (expr, args, complain, in_decl, false); + if (new_expr == error_mark_node) + return error_mark_node; + tree new_type = tsubst (type, args, complain, in_decl); + if (new_type == error_mark_node) + return error_mark_node; + return build_nt (ICONV_CONSTR, new_expr, new_type); +} + +/* Substitute ARGS into the argument deduction constraint T. */ + +tree +tsubst_argument_deduction_constr (tree t, tree args, tsubst_flags_t complain, + tree in_decl) +{ + cp_unevaluated guard; + tree expr = DEDUCT_CONSTR_EXPR (t); + tree pattern = DEDUCT_CONSTR_PATTERN (t); + tree autos = DEDUCT_CONSTR_PLACEHOLDER(t); + tree new_expr = tsubst_expr (expr, args, complain, in_decl, false); + if (new_expr == error_mark_node) + return error_mark_node; + /* It seems like substituting through the pattern will not affect the + placeholders. We should (?) be able to reuse the existing list + without any problems. If not, then we probably want to create a + new list of placeholders and then instantiate the pattern using + those. */ + tree new_pattern = tsubst (pattern, args, complain, in_decl); + if (new_pattern == error_mark_node) + return error_mark_node; + return build_nt (DEDUCT_CONSTR, new_expr, new_pattern, autos); +} + +/* Substitute ARGS into the exception constraint T. */ + +tree +tsubst_exception_constr (tree t, tree args, tsubst_flags_t complain, + tree in_decl) +{ + cp_unevaluated guard; + tree expr = EXCEPT_CONSTR_EXPR (t); + tree ret = tsubst_expr (expr, args, complain, in_decl, false); + if (ret == error_mark_node) + return error_mark_node; + return build_nt (EXCEPT_CONSTR, ret); +} /* A subroutine of tsubst_constraint_variables. Register local specializations for each of parameter in PARMS and its corresponding substituted constraint variable in VARS. Returns VARS. */ + tree declare_constraint_vars (tree parms, tree vars) { @@ -1553,6 +1740,7 @@ declare_constraint_vars (tree parms, tree vars) Note that the caller must establish a local specialization stack prior to calling this function since this substitution will declare the substituted parameters. */ + tree tsubst_constraint_variables (tree t, tree args, tsubst_flags_t complain, tree in_decl) @@ -1568,10 +1756,29 @@ tsubst_constraint_variables (tree t, tree args, 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) @@ -1627,6 +1834,8 @@ tsubst_nested_requirement (tree t, tree args, 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) { @@ -1662,7 +1871,8 @@ tsubst_requirement_body (tree t, tree args, r = tree_cons (NULL_TREE, e, r); t = TREE_CHAIN (t); } - return r; + /* Ensure that the order of constraints is the same as the original. */ + return nreverse (r); } } /* namespace */ @@ -1696,6 +1906,7 @@ tsubst_requires_expr (tree t, tree args, /* Substitute ARGS into the constraint information CI, producing a new constraint record. */ + tree tsubst_constraint_info (tree t, tree args, tsubst_flags_t complain, tree in_decl) @@ -1714,6 +1925,39 @@ tsubst_constraint_info (tree t, tree args, return build_constraints (tmpl_constr, decl_constr); } +/* Substitute ARGS into the constraint T. */ + +tree +tsubst_constraint (tree t, tree args, tsubst_flags_t complain, tree in_decl) +{ + if (t == NULL_TREE) + return t; + switch (TREE_CODE (t)) + { + case PRED_CONSTR: + return tsubst_predicate_constraint (t, args, complain, in_decl); + case CHECK_CONSTR: + return tsubst_check_constraint (t, args, complain, in_decl); + case CONJ_CONSTR: + case DISJ_CONSTR: + return tsubst_logical_operator (t, args, complain, in_decl); + case PARM_CONSTR: + return tsubst_parameterized_constraint (t, args, complain, in_decl); + case EXPR_CONSTR: + return tsubst_expr_constr (t, args, complain, in_decl); + case TYPE_CONSTR: + return tsubst_type_constr (t, args, complain, in_decl); + case ICONV_CONSTR: + return tsubst_implicit_conversion_constr (t, args, complain, in_decl); + case DEDUCT_CONSTR: + return tsubst_argument_deduction_constr (t, args, complain, in_decl); + case EXCEPT_CONSTR: + return tsubst_exception_constr (t, args, complain, in_decl); + default: + gcc_unreachable (); + } + return error_mark_node; +} /*--------------------------------------------------------------------------- Constraint satisfaction @@ -1738,11 +1982,14 @@ satisfy_pack_expansion (tree t, tree args, 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; - int n = TREE_VEC_LENGTH (exprs); - for (int i = 0; i < n; ++i) + /* 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; @@ -1760,12 +2007,14 @@ tree satisfy_predicate_constraint (tree t, tree args, tsubst_flags_t complain, tree in_decl) { - tree original = TREE_OPERAND (t, 0); + tree expr = TREE_OPERAND (t, 0); /* We should never have a naked pack expansion in a predicate constraint. */ - gcc_assert (TREE_CODE (original) != EXPR_PACK_EXPANSION); + gcc_assert (TREE_CODE (expr) != EXPR_PACK_EXPANSION); - tree expr = tsubst_expr (original, args, complain, in_decl, false); + /* 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; @@ -1781,8 +2030,37 @@ satisfy_predicate_constraint (tree t, tree args, return boolean_false_node; } - tree value = cxx_constant_value (expr); - return value; + return cxx_constant_value (expr); +} + +/* A concept check constraint like C<CARGS> is satisfied if substituting ARGS + into CARGS succeeds and C is satisfied for the resulting arguments. */ + +tree +satisfy_check_constraint (tree t, tree args, + tsubst_flags_t complain, tree in_decl) +{ + tree decl = CHECK_CONSTR_CONCEPT (t); + tree tmpl = DECL_TI_TEMPLATE (decl); + tree cargs = CHECK_CONSTR_ARGS (t); + + /* Instantiate the concept check arguments. */ + tree targs = tsubst (cargs, args, tf_none, NULL_TREE); + if (targs == error_mark_node) + return boolean_false_node; + + /* Search for a previous value. */ + if (tree prev = lookup_concept_satisfaction (tmpl, targs)) + return prev; + + /* Expand the concept; failure here implies non-satisfaction. */ + tree def = expand_concept (decl, targs); + if (def == error_mark_node) + return memoize_concept_satisfaction (tmpl, args, boolean_false_node); + + /* Recursively satisfy the constraint. */ + tree result = satisfy_constraint_1 (def, targs, complain, in_decl); + return memoize_concept_satisfaction (tmpl, targs, result); } /* Check an expression constraint. The constraint is satisfied if @@ -1803,7 +2081,6 @@ satisfy_expression_constraint (tree t, tree args, return boolean_false_node; if (!perform_deferred_access_checks (tf_none)) return boolean_false_node; - return boolean_true_node; } @@ -1822,7 +2099,6 @@ satisfy_type_constraint (tree t, tree args, return boolean_false_node; if (!perform_deferred_access_checks (complain)) return boolean_false_node; - return boolean_true_node; } @@ -1932,11 +2208,8 @@ satisfy_conjunction (tree t, tree args, tsubst_flags_t complain, tree in_decl) { tree t0 = satisfy_constraint_1 (TREE_OPERAND (t, 0), args, complain, in_decl); if (t0 == boolean_false_node) - return t0; - tree t1 = satisfy_constraint_1 (TREE_OPERAND (t, 1), args, complain, in_decl); - if (t1 == boolean_false_node) - return t1; - return boolean_true_node; + return boolean_false_node; + return satisfy_constraint_1 (TREE_OPERAND (t, 1), args, complain, in_decl); } /* Check that the disjunction of constraints is satisfied. Note @@ -1949,10 +2222,7 @@ satisfy_disjunction (tree t, tree args, tsubst_flags_t complain, tree in_decl) tree t0 = satisfy_constraint_1 (TREE_OPERAND (t, 0), args, complain, in_decl); if (t0 == boolean_true_node) return boolean_true_node; - tree t1 = satisfy_constraint_1 (TREE_OPERAND (t, 1), args, complain, in_decl); - if (t1 == boolean_true_node) - return boolean_true_node; - return boolean_false_node; + return satisfy_constraint_1 (TREE_OPERAND (t, 1), args, complain, in_decl); } /* Dispatch to an appropriate satisfaction routine depending on the @@ -1974,6 +2244,9 @@ satisfy_constraint_1 (tree t, tree args, tsubst_flags_t complain, tree in_decl) 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); @@ -2014,15 +2287,19 @@ satisfy_constraint_1 (tree t, tree args, tsubst_flags_t complain, tree in_decl) tree satisfy_constraint (tree t, tree args) { + auto_timevar time (TV_CONSTRAINT_SAT); + /* Turn off template processing. Constraint satisfaction only applies - to non-dependent terms, so we want full checking here. */ - processing_template_decl_sentinel sentinel (true); + 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); } @@ -2042,11 +2319,13 @@ satisfy_associated_constraints (tree ci, tree args) if (args && uses_template_parms (args)) return boolean_true_node; - /* Invalid requirements cannot be satisfied. */ - if (!valid_constraints_p (ci)) - return boolean_false_node; + /* Check if we've seen a previous result. */ + if (tree prev = lookup_constraint_satisfaction (ci, args)) + return prev; - return satisfy_constraint (CI_NORMALIZED_CONSTRAINTS (ci), args); + /* Actually test for satisfaction. */ + tree result = satisfy_constraint (CI_ASSOCIATED_CONSTRAINTS (ci), args); + return memoize_constraint_satisfaction (ci, args, result); } } /* namespace */ @@ -2059,7 +2338,7 @@ tree evaluate_constraints (tree constr, tree args) { gcc_assert (constraint_p (constr)); - return satisfy_constraint (normalize_constraint (constr), args); + return satisfy_constraint (constr, args); } /* Evaluate the function concept FN by substituting its own args @@ -2070,14 +2349,7 @@ evaluate_constraints (tree constr, tree args) tree evaluate_function_concept (tree fn, tree args) { - ++processing_template_decl; - /* We lift using DECL_TI_ARGS because we want to delay producing - non-dependent expressions until we're doing satisfaction. We can't just - go without any substitution because we need to lower the level of 'auto's - in type deduction constraints. */ - tree constr = transform_expression (lift_function_definition - (fn, DECL_TI_ARGS (fn))); - --processing_template_decl; + tree constr = build_nt (CHECK_CONSTR, fn, args); return satisfy_constraint (constr, args); } @@ -2087,12 +2359,9 @@ evaluate_function_concept (tree fn, tree args) boolean_false_node otherwise. */ tree -evaluate_variable_concept (tree decl, tree args) +evaluate_variable_concept (tree var, tree args) { - ++processing_template_decl; - tree constr = transform_expression (lift_variable_initializer - (decl, DECL_TI_ARGS (decl))); - --processing_template_decl; + tree constr = build_nt (CHECK_CONSTR, var, args); return satisfy_constraint (constr, args); } @@ -2103,9 +2372,7 @@ evaluate_variable_concept (tree decl, tree args) tree evaluate_constraint_expression (tree expr, tree args) { - ++processing_template_decl; - tree constr = transform_expression (lift_expression (expr)); - --processing_template_decl; + tree constr = normalize_expression (expr); return satisfy_constraint (constr, args); } @@ -2167,7 +2434,6 @@ constraint_expression_satisfied_p (tree expr, tree args) } /* namespace */ - /*--------------------------------------------------------------------------- Semantic analysis of requires-expressions ---------------------------------------------------------------------------*/ @@ -2311,6 +2577,7 @@ equivalently_constrained (tree d1, tree d2) ---------------------------------------------------------------------------*/ /* Returns true when the the constraints in A subsume those in B. */ + bool subsumes_constraints (tree a, tree b) { @@ -2334,6 +2601,7 @@ strictly_subsumes (tree a, tree b) Returns 1 if A is more constrained than B, -1 if B is more constrained than A, and 0 otherwise. */ + int more_constrained (tree d1, tree d2) { @@ -2350,6 +2618,7 @@ more_constrained (tree d1, tree d2) /* Returns true if D1 is at least as constrained as D2. That is, the associated constraints of D1 subsume those of D2, or both declarations are unconstrained. */ + bool at_least_as_constrained (tree d1, tree d2) { @@ -2361,49 +2630,71 @@ at_least_as_constrained (tree d1, tree d2) /*--------------------------------------------------------------------------- Constraint diagnostics + +FIXME: Normalize expressions into constraints before evaluating them. +This should be the general pattern for all such diagnostics. ---------------------------------------------------------------------------*/ -/* The diagnosis of constraints performs a combination of - normalization and satisfaction testing. We recursively - walk through the conjunction (or disjunctions) of associated - constraints, testing each sub-expression in turn. +/* The number of detailed constraint failures. */ - We currently restrict diagnostics to just the top-level - conjunctions within the associated constraints. A fully - recursive walk is possible, but it can generate a lot - of errors. */ +int constraint_errors = 0; +/* Do not generate errors after diagnosing this number of constraint + failures. -namespace { + FIXME: This is a really arbitrary number. Provide better control of + constraint diagnostics with a command line option. */ -void diagnose_expression (location_t, tree, tree); -void diagnose_constraint (location_t, tree, tree); +int constraint_thresh = 20; -/* Diagnose a conjunction of constraints. */ -void -diagnose_logical_operation (location_t loc, tree t, tree args) + +/* 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 () { - diagnose_expression (loc, TREE_OPERAND (t, 0), args); - diagnose_expression (loc, TREE_OPERAND (t, 0), args); + bool ret = constraint_thresh <= constraint_errors; + ++constraint_errors; + return ret; } -/* Determine if the trait expression T is satisfied by ARGS. - Emit a precise diagnostic if it is not. */ +/* Returns the number of undiagnosed errors. */ + +inline int +undiagnosed_constraint_failures () +{ + return constraint_errors - constraint_thresh; +} + +/* The diagnosis of constraints performs a combination of normalization + and satisfaction testing. We recursively walk through the conjunction or + disjunction of associated constraints, testing each sub-constraint in + turn. */ + +namespace { + +void diagnose_constraint (location_t, tree, tree, tree); + +/* Emit a specific diagnostics for a failed trait. */ + void -diagnose_trait_expression (location_t loc, tree t, tree args) +diagnose_trait_expression (location_t loc, tree, tree cur, tree args) { - if (constraint_expression_satisfied_p (t, args)) + if (constraint_expression_satisfied_p (cur, args)) + return; + if (elide_constraint_failure_p()) return; - /* Rebuild the trait expression so we can diagnose the - specific failure. */ + tree expr = PRED_CONSTR_EXPR (cur); ++processing_template_decl; - tree expr = tsubst_expr (t, args, tf_none, NULL_TREE, false); + expr = tsubst_expr (expr, args, tf_none, NULL_TREE, false); --processing_template_decl; tree t1 = TRAIT_EXPR_TYPE1 (expr); tree t2 = TRAIT_EXPR_TYPE2 (expr); - switch (TRAIT_EXPR_KIND (t)) + switch (TRAIT_EXPR_KIND (expr)) { case CPTK_HAS_NOTHROW_ASSIGN: inform (loc, " %qT is not nothrow copy assignable", t1); @@ -2473,93 +2764,52 @@ diagnose_trait_expression (location_t loc, tree t, tree args) } } -/* Determine if the call expression T, when normalized as a constraint, - is satisfied by ARGS. +/* Diagnose the expression of a predicate constraint. */ - TODO: If T is refers to a concept, We could recursively analyze - its definition to identify the exact failure, but that could - emit a *lot* of error messages (defeating the purpose of - improved diagnostics). Consider adding a flag to control the - depth of diagnostics. */ void -diagnose_call_expression (location_t loc, tree t, tree args) +diagnose_other_expression (location_t loc, tree, tree cur, tree args) { - if (constraint_expression_satisfied_p (t, args)) + if (constraint_expression_satisfied_p (cur, args)) return; - - /* Rebuild the expression for the purpose of diagnostics. */ - ++processing_template_decl; - tree expr = tsubst_expr (t, args, tf_none, NULL_TREE, false); - --processing_template_decl; - - /* If the function call is known to be a concept check, then - diagnose it differently (i.e., we may recurse). */ - if (resolve_constraint_check (t)) - inform (loc, " concept %qE was not satisfied", expr); - else - inform (loc, " %qE evaluated to false", expr); -} - -/* Determine if the template-id T, when normalized as a constraint - is satisfied by ARGS. */ -void -diagnose_template_id (location_t loc, tree t, tree args) -{ - /* Check for invalid template-ids. */ - if (!variable_template_p (TREE_OPERAND (t, 0))) - { - inform (loc, " invalid constraint %qE", t); - return; - } - - if (constraint_expression_satisfied_p (t, args)) + if (elide_constraint_failure_p()) return; + inform (loc, "%qE evaluated to false", cur); +} - /* Rebuild the expression for the purpose of diagnostics. */ - ++processing_template_decl; - tree expr = tsubst_expr (t, args, tf_none, NULL_TREE, false); - --processing_template_decl; +/* Do our best to infer meaning from predicates. */ - tree var = DECL_TEMPLATE_RESULT (TREE_OPERAND (t, 0)); - if (DECL_DECLARED_CONCEPT_P (var)) - inform (loc, " concept %qE was not satisfied", expr); +inline void +diagnose_predicate_constraint (location_t loc, tree orig, tree cur, tree args) +{ + if (TREE_CODE (PRED_CONSTR_EXPR (cur)) == TRAIT_EXPR) + diagnose_trait_expression (loc, orig, cur, args); else - inform (loc, " %qE evaluated to false", expr); + diagnose_other_expression (loc, orig, cur, args); } -/* Determine if the requires-expression, when normalized as a - constraint is satisfied by ARGS. +/* Diagnose a failed pack expansion, possibly containing constraints. */ - TODO: Build sets of expressions, types, and constraints - based on the requirements in T and emit specific diagnostics - for those. */ void -diagnose_requires_expression (location_t loc, tree t, tree args) +diagnose_pack_expansion (location_t loc, tree, tree cur, tree args) { - if (constraint_expression_satisfied_p (t, args)) + if (constraint_expression_satisfied_p (cur, args)) return; - inform (loc, "requirements not satisfied"); -} - -void -diagnose_pack_expansion (location_t loc, tree t, tree args) -{ - if (constraint_expression_satisfied_p (t, args)) + if (elide_constraint_failure_p()) return; /* Make sure that we don't have naked packs that we don't expect. */ - if (!same_type_p (TREE_TYPE (t), boolean_type_node)) + if (!same_type_p (TREE_TYPE (cur), boolean_type_node)) { - inform (loc, "invalid pack expansion in constraint %qE", t); + inform (loc, "invalid pack expansion in constraint %qE", cur); return; } - inform (loc, " in the expansion of %qE", t); + 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 (t, args, tf_none, NULL_TREE); + tree exprs = tsubst_pack_expansion (cur, args, tf_none, NULL_TREE); --processing_template_decl; if (exprs == error_mark_node) { @@ -2578,82 +2828,276 @@ diagnose_pack_expansion (location_t loc, tree t, tree args) } } -/* Diagnose an expression that would be characterized as - a predicate constraint. */ +/* Diagnose a potentially unsatisfied concept check constraint DECL<CARGS>. + Parameters are as for diagnose_constraint. */ + +void +diagnose_check_constraint (location_t loc, tree orig, tree cur, tree args) +{ + if (constraints_satisfied_p (cur, args)) + return; + + tree decl = CHECK_CONSTR_CONCEPT (cur); + tree cargs = CHECK_CONSTR_ARGS (cur); + tree tmpl = DECL_TI_TEMPLATE (decl); + tree check = build_nt (CHECK_CONSTR, decl, cargs); + + /* Instantiate the concept check arguments. */ + tree targs = tsubst (cargs, args, tf_none, NULL_TREE); + if (targs == error_mark_node) + { + if (elide_constraint_failure_p ()) + return; + inform (loc, "invalid use of the concept %qE", check); + tsubst (cargs, args, tf_warning_or_error, NULL_TREE); + return; + } + + tree sub = build_tree_list (tmpl, targs); + /* Update to the expanded definitions. */ + cur = expand_concept (decl, targs); + if (cur == error_mark_node) + { + if (elide_constraint_failure_p ()) + return; + inform (loc, "in the expansion of concept %qE %S", check, sub); + cur = get_concept_definition (decl); + tsubst_expr (cur, targs, tf_warning_or_error, NULL_TREE, false); + return; + } + + orig = get_concept_definition (CHECK_CONSTR_CONCEPT (orig)); + orig = normalize_expression (orig); + + location_t dloc = DECL_SOURCE_LOCATION (decl); + inform (dloc, "within %qS", sub); + diagnose_constraint (dloc, orig, cur, targs); +} + +/* Diagnose a potentially unsatisfied conjunction or disjunction. Parameters + are as for diagnose_constraint. */ + void -diagnose_other_expression (location_t loc, tree t, tree args) +diagnose_logical_constraint (location_t loc, tree orig, tree cur, tree args) { - if (constraint_expression_satisfied_p (t, 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; - inform (loc, " %qE evaluated to false", t); + if (!constraints_satisfied_p (t1, args)) + diagnose_constraint (loc, TREE_OPERAND (orig, 1), t1, args); } +/* Diagnose a potential expression constraint failure. */ + void -diagnose_expression (location_t loc, tree t, tree args) +diagnose_expression_constraint (location_t loc, tree orig, tree cur, tree args) { - switch (TREE_CODE (t)) - { - case TRUTH_ANDIF_EXPR: - diagnose_logical_operation (loc, t, args); - break; + if (constraints_satisfied_p (cur, args)) + return; + if (elide_constraint_failure_p()) + return; - case TRUTH_ORIF_EXPR: - diagnose_logical_operation (loc, t, args); - break; + tree expr = EXPR_CONSTR_EXPR (orig); + inform (loc, "the required expression %qE would be ill-formed", expr); - case CALL_EXPR: - diagnose_call_expression (loc, t, args); - break; + // TODO: We should have a flag that controls this substitution. + // I'm finding it very useful for resolving concept check errors. - case TEMPLATE_ID_EXPR: - diagnose_template_id (loc, t, args); - break; + // inform (input_location, "==== BEGIN DUMP ===="); + // tsubst_expr (EXPR_CONSTR_EXPR (orig), args, tf_warning_or_error, NULL_TREE, false); + // inform (input_location, "==== END DUMP ===="); +} - case REQUIRES_EXPR: - diagnose_requires_expression (loc, t, args); - break; +/* Diagnose a potentially failed type constraint. */ - case TRAIT_EXPR: - diagnose_trait_expression (loc, t, args); - break; +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; - case EXPR_PACK_EXPANSION: - diagnose_pack_expansion (loc, t, args); - break; + tree type = TYPE_CONSTR_TYPE (orig); + inform (loc, "the required type %qT would be ill-formed", type); +} - default: - diagnose_other_expression (loc, t, args); - break; +/* 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); } -inline void -diagnose_predicate_constraint (location_t loc, tree t, tree args) +/* Diagnose an argument deduction constraint. */ + +void +diagnose_argument_deduction_constraint (location_t loc, tree orig, tree cur, + tree args) { - diagnose_expression (loc, PRED_CONSTR_EXPR (t), args); + if (constraints_satisfied_p (cur, args)) + return; + + /* The expression and type will previously have been substituted into, + and therefore may already be an error. Also, we will have already + diagnosed substution failures into an expression since this must be + part of a compound requirement. */ + tree expr = DEDUCT_CONSTR_EXPR (cur); + if (error_operand_p (expr)) + return; + + /* Don't elide a previously diagnosed failure. */ + if (elide_constraint_failure_p ()) + return; + + tree pattern = DEDUCT_CONSTR_PATTERN (cur); + if (error_operand_p (pattern)) + { + inform (loc, "substitution into type %qT failed", + DEDUCT_CONSTR_PATTERN (orig)); + return; + } + + inform (loc, "unable to deduce placeholder type %qT from %qE", + pattern, expr); } -inline void -diagnose_conjunction (location_t loc, tree t, tree args) +/* Diagnose an exception constraint. */ + +void +diagnose_exception_constraint (location_t loc, tree orig, tree cur, tree args) { - diagnose_constraint (loc, TREE_OPERAND (t, 0), args); - diagnose_constraint (loc, TREE_OPERAND (t, 1), 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 the constraint T for the given ARGS. This is only - ever invoked on the associated constraints, so we can - only have conjunctions of predicate constraints. */ +/* Diagnose a potentially unsatisfied parameterized constraint. */ + void -diagnose_constraint (location_t loc, tree t, tree args) +diagnose_parameterized_constraint (location_t loc, tree orig, tree cur, + tree args) { - switch (TREE_CODE (t)) + if (constraints_satisfied_p (cur, args)) + return; + + local_specialization_stack stack; + tree parms = PARM_CONSTR_PARMS (cur); + tree vars = tsubst_constraint_variables (parms, args, tf_warning_or_error, + NULL_TREE); + if (vars == error_mark_node) + { + if (elide_constraint_failure_p ()) + return; + + /* TODO: Check which variable failed and use orig to diagnose + that substitution error. */ + inform (loc, "failed to instantiate constraint variables"); + return; + } + + /* TODO: It would be better write these in a list. */ + while (vars) { + inform (loc, " with %q#D", vars); + vars = TREE_CHAIN (vars); + } + orig = PARM_CONSTR_OPERAND (orig); + cur = PARM_CONSTR_OPERAND (cur); + return diagnose_constraint (loc, orig, cur, args); +} + +/* Diagnose the constraint CUR for the given ARGS. This is only ever invoked + on the associated constraints, so we can only have conjunctions of + predicate constraints. The ORIGinal (dependent) constructs follow + the current constraints to enable better diagnostics. Note that ORIG + and CUR must be the same kinds of node, except when CUR is an error. */ + +void +diagnose_constraint (location_t loc, tree orig, tree cur, tree args) +{ + switch (TREE_CODE (cur)) + { + case EXPR_CONSTR: + diagnose_expression_constraint (loc, orig, cur, args); + break; + + case TYPE_CONSTR: + diagnose_type_constraint (loc, orig, cur, args); + break; + + case ICONV_CONSTR: + diagnose_implicit_conversion_constraint (loc, orig, cur, args); + break; + + case DEDUCT_CONSTR: + diagnose_argument_deduction_constraint (loc, orig, cur, args); + break; + + case EXCEPT_CONSTR: + diagnose_exception_constraint (loc, orig, cur, args); + break; + case CONJ_CONSTR: - diagnose_conjunction (loc, t, args); + case DISJ_CONSTR: + diagnose_logical_constraint (loc, orig, cur, args); break; case PRED_CONSTR: - diagnose_predicate_constraint (loc, t, args); + 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: @@ -2678,16 +3122,10 @@ diagnose_declaration_constraints (location_t loc, tree decl, tree args) args = TI_ARGS (ti); } - /* Check that the constraints are actually valid. */ - tree ci = get_constraints (decl); - if (!valid_constraints_p (ci)) - { - inform (loc, " invalid constraints"); - return; - } - /* Recursively diagnose the associated constraints. */ - diagnose_constraint (loc, CI_ASSOCIATED_CONSTRAINTS (ci), args); + tree ci = get_constraints (decl); + tree t = CI_ASSOCIATED_CONSTRAINTS (ci); + diagnose_constraint (loc, t, t, args); } } // namespace @@ -2699,8 +3137,17 @@ diagnose_declaration_constraints (location_t loc, tree decl, tree args) void diagnose_constraints (location_t loc, tree t, tree args) { + constraint_errors = 0; + if (constraint_p (t)) - diagnose_constraint (loc, t, args); - else + diagnose_constraint (loc, t, t, args); + else if (DECL_P (t)) diagnose_declaration_constraints (loc, t, args); + else + gcc_unreachable (); + + /* Note the number of elided failures. */ + int n = undiagnosed_constraint_failures (); + if (n > 0) + inform (loc, "... and %d more constraint errors not shown", n); } |